suppressPackageStartupMessages({
library(ArchR)
library(tidyverse)
library(SingleCellExperiment)
library(zellkonverter)
library(dtwclust)
})
proj <- loadArchRProject("11_added_Ricards_peaks_p2g_entire_chromosome", showLogo = FALSE)
## Successfully loaded ArchRProject!
#saveArchRProject(proj, "/omics/groups/OE0533/internal/katharina/scDoRI/gastrulation_data/11_added_Ricards_peaks_p2g_entire_chromosome")
P2G-link matrix
In order to get gene regulatory links, one can compute a simple correlation between accessible peaks and gene expression, in order to find peaks whose activity is highly correlated with expression of a certain gene. These are potential enhancers of a gene. Since single cell data is very sparse, the common way to compute correlatiosn is by aggregating accessibility and gene expression data across cell aggregates. In ArchR this is done by sampling 500 cells from the entire dataset and finding the 50 nearest neighbors of these cells. These cell aggregates therefore reprsent groups of similar cells and can be used to compute correlations.
Notably, for computing links within a certain distance on the chromosome, ArchR does not take into consideration the strand orientation, but computes the distance between the “start” coordinate and the peak middle coordinate. However, on the minus strand the TSS is the “end” coordinate. For the correlations this is not important, but in my computations I will use the TSS coordinate in a strand-aware fashion.
Based on these putative peak-to-gene links, it is possible to compute gene activity scores. If these scores recapitulate gene expression well, this is a validation of the links. Nevertheless, it is expected that gene activity scores correlate highly with gene expression, since the peaks used for the computation are highly correlated with the genes per definition. In the following you will find a function to compute gene activity scores from peak-to-gene links, adpated from Cicero, where gene activit scores are computed from co-accessible peaks.
Furthermore, since peaks which are farther away from a gene on the genome are less likely to regulate this gene, it is common to use distance weigths to penalize peaks which are highly correlated, but distant. However, I observed that using distance weigths decreases the correlation with gene expression, with less steep decay rates resulting in better gene activity scores. This shows that the distance weights lead to a lot of zero values when multiplied with the correlation values. Still, biologically very distant peaks are probably not correlated with a promoter, because they are intereacting, but more likely, are arbitrary correlations. Finding a good trade-off between considering correlations across large distances, but also using prior knowledge about the biology and restricting the links to a certain window is non-trivial. The correct distance decay rate probably depends on each individual gene and might differ across celltypes.
Yet another approach would be to use only peaks, which are within +/- 100kb of the TSS of a gene, thereby, removing any peaks which are far away. This is similar to the approach in ArchR. Here, the gene activity scores for each gene are computed based on all peaks which are within +/- 100bp of the TSS of the gene. As you will see in the following plots using this approach leads to very high correlations between gene expression and gene activity scores. Computing the scores based on peak-to-gene links offers only a minor improvement.
The main purpose of the entire excercise was to be able to compare the goodness of these links to links obtained using scDoRi. This relationship will have to be explored further.
proj <- addPeak2GeneLinks(ArchRProj = proj,
reducedDims = "atac_LSI_100000",
useMatrix = "GeneExpressionMatrix",
maxDist = 400000000,
verbose = TRUE
)
p2g <- getPeak2GeneLinks(
ArchRProj = proj,
corCutOff = -1,
resolution = 1,
FDRCutOff = 1e-04,
varCutOffATAC = .25,
varCutOffRNA = .25,
returnLoops = FALSE
)
#saveRDS(p2g, "/omics/groups/OE0533/internal/katharina/scDoRI/gastrulation_data/Rmds/peak2gene_links_entire_chromosome_25_04_2022")
p2g <- readRDS( "/omics/groups/OE0533/internal/katharina/scDoRI/gastrulation_data/Rmds/peak2gene_links_entire_chromosome_25_04_2022")
Read in the peak accessibility matrix and the gene expression matrix:
# get peak matrix
peaks <- getMatrixFromProject(proj, useMatrix = "PeakMatrix", binarize = FALSE)
## ArchR logging to : ArchRLogs/ArchR-getMatrixFromProject-1e8d1806ff55-Date-2022-05-30_Time-13-04-09.log
## If there is an issue, please report to github with logFile!
## 2022-05-30 13:24:07 : Organizing colData, 19.977 mins elapsed.
## 2022-05-30 13:24:08 : Organizing rowData, 19.983 mins elapsed.
## 2022-05-30 13:24:08 : Organizing rowRanges, 19.984 mins elapsed.
## 2022-05-30 13:24:08 : Organizing Assays (1 of 1), 19.984 mins elapsed.
## 2022-05-30 13:26:15 : Constructing SummarizedExperiment, 22.099 mins elapsed.
## 2022-05-30 13:26:19 : Finished Matrix Creation, 22.163 mins elapsed.
peak_mat <- assays(peaks)[[1]]
# read in gne expresssion matrix
gene_expr <- getMatrixFromProject(proj,
useMatrix = "GeneExpressionMatrix")
## ArchR logging to : ArchRLogs/ArchR-getMatrixFromProject-1e8d56cfc1a7-Date-2022-05-30_Time-13-26-19.log
## If there is an issue, please report to github with logFile!
## 2022-05-30 13:49:06 : Organizing colData, 22.793 mins elapsed.
## 2022-05-30 13:49:06 : Organizing rowData, 22.798 mins elapsed.
## 2022-05-30 13:49:06 : Organizing rowRanges, 22.798 mins elapsed.
## 2022-05-30 13:49:06 : Organizing Assays (1 of 1), 22.798 mins elapsed.
## 2022-05-30 13:52:38 : Constructing SummarizedExperiment, 26.331 mins elapsed.
## 2022-05-30 13:52:44 : Finished Matrix Creation, 26.428 mins elapsed.
expr_mat <- assays(gene_expr)[[1]]
rownames(expr_mat) <- rowData(gene_expr)$name
# read in archr gene activity scores
archr_scores <- getMatrixFromProject(proj, useMatrix = "GeneScoreMatrix")
## ArchR logging to : ArchRLogs/ArchR-getMatrixFromProject-1e8d13343afa-Date-2022-05-30_Time-13-52-44.log
## If there is an issue, please report to github with logFile!
## 2022-05-30 14:11:02 : Organizing colData, 18.29 mins elapsed.
## 2022-05-30 14:11:02 : Organizing rowData, 18.296 mins elapsed.
## 2022-05-30 14:11:02 : Organizing rowRanges, 18.296 mins elapsed.
## 2022-05-30 14:11:02 : Organizing Assays (1 of 1), 18.296 mins elapsed.
## 2022-05-30 14:14:01 : Constructing SummarizedExperiment, 21.285 mins elapsed.
## 2022-05-30 14:14:06 : Finished Matrix Creation, 21.362 mins elapsed.
cp_names <- colnames(colData(archr_scores))
cp_names[20] <- "celltypes"
colnames(colData(archr_scores)) <- cp_names
archr_scores_mat <- assays(archr_scores)[[1]]
rownames(archr_scores_mat) <- rowData(archr_scores)$name
We will only use peaks linked to highly variable genes to compute gene activity scores.
hvg_list <- read.table("jupyter_notebooks/hvg_list", sep = ",")$x
# get RNA index of hvg
meta_rna <- rowData(gene_expr) %>% as.data.frame() %>% mutate(row_index = seq(nrow(.)))
idx <- (meta_rna %>% filter(name %in% hvg_list))$row_index
expr_sub <- expr_mat[idx, ]
seacells <- read_csv("jupyter_notebooks/SEACell_files/SEACell_assignment")
## Rows: 45991 Columns: 2
## ── Column specification ─────────────────────────────────────────────────────
## Delimiter: ","
## chr (2): index, SEACell
##
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
links <- p2g %>% as.data.frame() %>%
filter(Correlation > 0.2) %>%
filter(idxRNA %in% idx)
stopifnot(all(links$Correlation > 0))
Create a p2g link matrix
p2g_mat <- sparseMatrix(i = links$idxRNA,
j = links$idxATAC,
x= links$Correlation,
dims = c(dim(expr_mat)[1],
dim(peak_mat)[1]))
rownames(p2g_mat) <- rowData(gene_expr)$name
rownames(peak_mat) <- seq.int(dim(peak_mat)[1])
colnames(p2g_mat) <- seq.int(dim(peak_mat)[1])
Filter and prepare peak matrix and p2g links matrix:
# remove columns of peaks which are not linked to any peak
p2g_mat_sub <- p2g_mat[, colSums(p2g_mat) != 0]
# use only highly variable genes
p2g_mat_sub <- p2g_mat_sub[hvg_list, ]
# remove any genes which are not linked to any peak
p2g_mat_sub <- p2g_mat_sub[rowSums(p2g_mat_sub) != 0, ]
stopifnot(all(rownames(p2g_mat_sub) %in% hvg_list))
stopifnot(any(is.na(p2g_mat_sub) == FALSE))
# keep only peaks which are linked to genes in the accessibility matrix
peak_mat_sub <- peak_mat[colnames(p2g_mat_sub), ]
stopifnot(rownames(peak_mat_sub) == colnames(p2g_mat_sub))
#stopifnot(any(is.na(peak_mat_sub) == FALSE))
stopifnot(dim(peak_mat_sub)[1] == dim(p2g_mat_sub)[2])
expr_mat_sub <- expr_mat[as.vector(rownames(p2g_mat_sub)), ]
Function to compute gene activity scores
gene_activity_scores <- function(peak_mat, p2g_mat) {
#peak_mat_subset <- peak_mat[colnames(p2g_mat), ]
# normalize the p2g matrix by the total number of peaks linked to each gene
p2g_mat <- p2g_mat / rowSums(p2g_mat)
print(paste0("normalized the p2g matrix"))
stopifnot(any(is.na(p2g_mat)) == FALSE)
# Now we can compute a weighted sum of peak2gene correlations for each
# peak and gene
scores <- p2g_mat %*% peak_mat
print(paste0("Computed weightes sum of peaks for each gene and cell"))
# create a dataframe for computing the linear model
linear_model_df <- data.frame(cell = colnames(scores),
total_activity = colSums(scores),
total_sites = colSums(peak_mat))
# compute a linear model
activity_model <- stats::lm(log(total_activity) ~ log(total_sites),
data = linear_model_df)
# extract the fitted model
linear_model_df$fitted_curve <- exp(as.vector(predict(activity_model,
type = "response")))
# compute size factors from fitted model
size_factors <- mean(linear_model_df$fitted_curve) / linear_model_df$fitted_curve
# create diagonal matrix containing the size factors
size_factors_mat <- Matrix::Diagonal(x = size_factors)
#row.names(size_factors_mat) <- linear_model_df$cell
# normalize by library depth size factors
norm_scores <- Matrix::t(size_factors_mat %*% Matrix::t(scores))
print(paste0("Normalized for library size"))
# exponentiate, because RNA counts are log-normally distributed
norm_scores@x <- pmin(1e9, exp(norm_scores@x) - 1)
print(paste0("Exponentiated matrix"))
# free some memory
#rm(peak_mat_subset)
rm(activity_model)
rm(scores)
gc(reset = TRUE)
# scale with total activity scores again
scale_factors <- Matrix::Diagonal(x = 1/Matrix::colSums(norm_scores))
print(paste0("Divided by total activity to get value between zero and one"))
final_scores <- Matrix::t(scale_factors %*% Matrix::t(norm_scores))
return(final_scores)
}
p2g_scores <- gene_activity_scores(peak_mat_sub, p2g_mat_sub)
## [1] "normalized the p2g matrix"
## [1] "Computed weightes sum of peaks for each gene and cell"
## [1] "Normalized for library size"
## [1] "Exponentiated matrix"
## [1] "Divided by total activity to get value between zero and one"
#saveRDS(p2g_scores, "ArchR_p2g_based_gene_activity_scores")
TODO: Add functions here, add ArchR aggregates and compute correlations as scatter & density plot
Example of p2g links within 250kb
cp_names <- colnames(colData(gene_expr))
cp_names[20] <- "celltypes"
colnames(colData(gene_expr)) <- cp_names
#rownames(expr_mat) <- rowData(gene_expr)$name
genes <- expr_mat[as.vector(rownames(p2g_scores)), ]
stopifnot(any(rownames(genes) == rownames(p2g_scores)))
# create matrix to store aggregates
expr_agg <- matrix(data = 0,
nrow = dim(genes)[1],
ncol = length(unique(colData(gene_expr)$celltypes)),
dimnames = list(rownames(p2g_scores),
unique(colData(gene_expr)$celltypes)))
# fill matrix
for (celltype in unique(colData(gene_expr)$celltypes)){
barcodes <- rownames(colData(gene_expr) %>%
as.data.frame() %>%
filter(celltypes == celltype))
expr_agg[, celltype] <- rowSums(genes[, barcodes])
}
p2g_score_agg <- matrix(data = 0,
nrow = dim(p2g_scores)[1],
ncol = length(unique(colData(gene_expr)$celltypes)),
dimnames = list(rownames(p2g_scores),
unique(colData(gene_expr)$celltypes)))
for (celltype in unique(colData(gene_expr)$celltypes)){
barcodes <- rownames(colData(gene_expr) %>%
as.data.frame() %>%
filter(celltypes == celltype))
p2g_score_agg[, celltype] <- rowSums(p2g_scores[, barcodes])
}
Correlations between aggregated gene expression and aggregated p2g scores for celltypes.
correlations_250kb = c()
for (i in seq.int(dim(p2g_score_agg)[1])){
rowa <- expr_agg[i, ]
rowa <- rowa - mean(rowa)
rowa <- rowa / sd(rowa)
rowb <- p2g_score_agg[i, ]
rowb <- rowb - mean(rowb)
rowb <- rowb / sd(rowb)
corr_value = mean(rowa * rowb)
correlations_250kb <- c(correlations_250kb, corr_value)
}
names(correlations_250kb) <- rownames(p2g_score_agg)
plot_250kb <- ggplot() + geom_histogram(aes(x = correlations_250kb), bins = 200, fill="#69b3a2") +
labs(title = "Gene activity scores computed based on p2g links on entire chromosome")
plot_250kb

KNN cell aggregates from ArchR
To compute peak-to-gene links, cell aggregates are created to overcome sparsity as described above. To create cell aggregates, 500 cells are randomly sampled from a low-dimensional embedding, eg. PCA space and the 50 nearest neighbors of each cell are determined. This way ArchR creates 500 aggregates, each consisting of 50 cells. Below, I used these aggregates for computing correlations between different gene activity scores, however the results did not look very usable, since a lot of negative correlations were obtained this way. This might be due to theses aggregates not being representative, too small or too few. I decided to not use these aggreagates, but instaed used SEACells, which is a tool for creating cell aggregates (“metacells”).
rna_knn <- readRDS("11_added_Ricards_peaks/Peak2GeneLinks/seRNA-Group-KNN.rds")
rna_agg_mat <- assays(rna_knn)[[1]]
rownames(rna_agg_mat) <- rowData(rna_knn)$name
cell_agg_list <- metadata(rna_knn)[[1]]
knn_aggregates <- function(matrix, cell_agg_list){
# empty matrix to store aggregates
agg <- matrix(data = 0,
nrow = dim(matrix)[1],
ncol = length(cell_agg_list),
dimnames = list(rownames(matrix), NULL))
for (i in seq.int(length(cell_agg_list))) {
agg[, i] <- rowSums(matrix[, cell_agg_list[[i]]])
}
return(agg)
}
rna_agg <- knn_aggregates(expr_mat_sub, cell_agg_list)
agg_p2g_knn <- knn_aggregates(p2g_scores, cell_agg_list)
archr_knn <- archr_scores_mat[as.vector(rownames(agg_p2g_knn)),]
agg_archr_knn <- knn_aggregates(archr_knn, cell_agg_list)
archr_knn <- rowwise_correlations(rna_agg, agg_archr_knn, "Archr gene activity scores")
p2g_knn <- rowwise_correlations(rna_agg, agg_p2g_knn, "Peak-to-gene links activity scores")
cowplot::plot_grid(archr_knn[[2]], p2g_knn[[2]], ncol = 2)
ggplot() + geom_density_2d_filled(aes(x = p2g_knn[[1]],
y = archr_knn[[1]]), alpha = .5) +
geom_point(aes(x = p2g_knn[[1]], y = archr_knn[[1]])) +
geom_line(aes(x = p2g_knn[[1]], y = p2g_knn[[1]]), col = "red")
theme(legend.position = "None")
Functions
Function to prep peak accessibility matrix, gene expression matrix and p2g-link matrix
prep_peak_p2g <- function(peak_mat, p2g_mat, hvg_list, expr_mat){
#rownames(peak_mat) <- seq.int(dim(peak_mat)[1])
#colnames(p2g_mat) <- seq.int(dim(p2g_mat)[2])
# remove columns of peaks which are not linked to any peak
p2g_mat_sub <- p2g_mat[, colSums(p2g_mat) != 0]
# use only highly variable genes
p2g_mat_sub <- p2g_mat_sub[hvg_list, ]
# remove any genes which are not linked to any peak
p2g_mat_sub <- p2g_mat_sub[rowSums(p2g_mat_sub) != 0, ]
stopifnot(all(rownames(p2g_mat_sub) %in% hvg_list))
stopifnot(any(is.na(p2g_mat_sub) == FALSE))
# keep only peaks which are linked to genes in the accessibility matrix
peak_mat_sub <- peak_mat[colnames(p2g_mat_sub), ]
stopifnot(rownames(peak_mat_sub) == colnames(p2g_mat_sub))
stopifnot(any(is.na(peak_mat_sub) == FALSE))
stopifnot(dim(peak_mat_sub)[1] == dim(p2g_mat_sub)[2])
expr_mat_sub <- expr_mat[as.vector(rownames(p2g_mat_sub)), ]
stopifnot(rownames(expr_mat_sub) == rownames(p2g_mat_sub))
return(list(peak_mat_sub, p2g_mat_sub, expr_mat_sub))
}
Function to create aggregate matrices:
# the data matrix needs to be of dimension features x cells
# the column of the colData of the sce object where celltypes are stored
# needs to be called "celltypes"
create_celltype_aggregates <- function(sce, data_matrix, celltypes) {
#create empty matrix to store aggregates
agg <- matrix(data = 0,
nrow = nrow(data_matrix),
ncol = length(celltypes),
dimnames = list(rownames(data_matrix), celltypes))
for (celltype in celltypes) {
barcodes <- rownames(colData(sce) %>%
as.data.frame() %>%
filter(celltypes == celltype))
agg[, celltype] <- rowSums(data_matrix[, barcodes])
}
return(agg)
}
create_celltype_aggregates_p2g_scores <- function(gene_expr_sce, p2g_score_matrix, celltypes) {
#create empty matrix to store aggregates
agg <- matrix(data = 0,
nrow = nrow(p2g_score_matrix),
ncol = length(celltypes),
dimnames = list(rownames(p2g_score_matrix), celltypes))
for (celltype in celltypes) {
barcodes <- rownames(colData(gene_expr_sce) %>%
as.data.frame() %>%
filter(celltypes == celltype))
agg[, celltype] <- rowSums(p2g_score_matrix[, barcodes])
}
return(agg)
}
create_seacell_aggregates <- function(data_matrix, seacells_df){
agg <- matrix(data = 0,
nrow = nrow(data_matrix),
ncol = length(unique(seacells_df$SEACell)),
dimnames = list(rownames(data_matrix),
unique(seacells_df$SEACell)))
#stopifnot(nrow(agg) == nrow(data_matrix))
for (seacell in unique(seacells_df$SEACell)){
#print(seacell)
barcodes <- (seacells_df %>% filter(SEACell == seacell))$index
#print(barcodes)
if (length(barcodes) == 1){
agg[, seacell] <- data_matrix[, barcodes]
} else{
agg[, seacell] <- rowSums(data_matrix[, barcodes])
}
}
return(agg)
}
Function to compute row-wise correlations between two matrices:
rowwise_correlations <- function(MatrixA, MatrixB, name) {
intersect_genes <- intersect(rownames(MatrixA), rownames(MatrixB))
MatrixA <- MatrixA[intersect_genes, ]
MatrixB <- MatrixB[intersect_genes, ]
correlations <- c()
for (i in seq.int(dim(MatrixA)[1])) {
rowA <- MatrixA[i, ]
rowA <- rowA - mean(rowA)
if (sd(rowA) != 0) {
rowA <- rowA / sd(rowA)
}
rowB <- MatrixB[i, ]
rowB <- rowB - mean(rowB)
if (sd(rowB) != 0){
rowB <- rowB / sd(rowB)
}
corr_value <- mean(rowA * rowB)
correlations <- c(correlations, corr_value)
}
names(correlations) <- rownames(MatrixA)
plot <- ggplot() + geom_histogram(aes(x = correlations),
bins = 200,
fill="#69b3a2") + labs(title = paste0(name))
return(list(correlations, plot))
}
Function for ArchR KNN aggregates
# Function to compute aggregates with knn from ArchR
knn_aggregates <- function(matrix, cell_agg_list){
# empty matrix to store aggregates
agg <- matrix(data = 0,
nrow = dim(matrix)[1],
ncol = length(cell_agg_list),
dimnames = list(rownames(matrix), NULL))
for (i in seq.int(length(cell_agg_list))) {
agg[, i] <- rowSums(matrix[, cell_agg_list[[i]]])
}
return(agg)
}
Celltype aggregates
ArchR gene activity scores
To compute the correlations between gene expression and ArchR gene activity scores I first aggregated cells according to celltypes to compute correlations. As can be seen in the plot below, this yields very high correlation values. This is as expected, since in (Granja JM 2021) the authors compared 52 different ways of computing gene activity scores from ATAC-seq data and found their method to be the best one.
archr_scores_sub <- archr_scores_mat[as.vector(rownames(expr_mat_sub)), ]
name <- "ArchR_scores, Celltype aggregates"
archr_scores_agg <- create_celltype_aggregates(archr_scores, archr_scores_sub,
unique(colData(archr_scores)$celltypes))
stopifnot(any(is.na(archr_scores_agg)) == FALSE)
corrs <- rowwise_correlations(expr_agg, archr_scores_agg, name)
archr_corr <- corrs[1]
cowplot::plot_grid(plot_250kb + labs(title = "P2g-links activity scores, Celltype aggregates"), corrs[[2]], ncol = 2)

#, fig.width = 5, fig.height=5}
ggplot() + #geom_density2d_filled(aes(x = correlations_250kb, y = corrs[1])) #+
geom_point(aes(x = correlations_250kb, y = corrs[[1]])) +
geom_density_2d_filled(aes(x = correlations_250kb, y = corrs[[1]]), alpha = 0.5) +
geom_line(aes(x = corrs[[1]], corrs[[1]]), color = "red") +
labs(x = "Correlation gene expression and p2g activity scores",
y = "Correlation gene expression and ArchR gene activity scores",
title = "Celltype aggregates") +
theme(legend.position = "None")

# ggplot() + geom_point(aes(x = archr_scores_sub["Hba-a1",], y = p2g_scores["Hba-a1",]))
# ggplot() + geom_point(aes(x = archr_scores_sub["Gata6",], y = p2g_scores["Hba-a1",]))
SEACell aggregates
Instead of using celltype aggregates as above, another option is to use SEACells as described in (Persad et al. 2022). These were computed using Python and the resulting cell aggregates (“metacells”) are used for aggregating gene expression and gene activity scores below. The correlations when using SEACells are much higher than the correlations obtained using the ArchR cell aggregates. For this reason I will use SEACells for computing correlations in the following steps.
seacells <- seacells %>% filter(index %in% colnames(expr_mat))
stopifnot(nrow(p2g_scores) == nrow(expr_mat_sub))
seacell_p2g_agg <- create_seacell_aggregates(p2g_scores, seacells)
seacell_rna_agg <- create_seacell_aggregates(expr_mat_sub, seacells)
seacell_archr_agg <- create_seacell_aggregates(archr_scores_sub, seacells)
seacell_corr_p2g <- rowwise_correlations(seacell_rna_agg , seacell_p2g_agg,
"P2g links of entire chromosome, SEAcells" )
seacell_corr_archr <- rowwise_correlations(seacell_rna_agg, seacell_archr_agg,
"ArchR gene activity scores, SEAcells")
cowplot::plot_grid(seacell_corr_p2g[[2]], seacell_corr_archr[[2]], ncol = 2)

ggplot() + #geom_density2d_filled(aes(x = correlations_250kb, y = corrs[1])) #+
geom_point(aes(x = seacell_corr_p2g[[1]], y = seacell_corr_archr[[1]])) +
geom_density_2d_filled(aes(x = seacell_corr_p2g[[1]], y = seacell_corr_archr[[1]]), alpha = 0.5) +
geom_line(aes(x = seacell_corr_archr[[1]], y = seacell_corr_archr[[1]]), color = "red" ) +
labs(x = "Correlation gene expression and p2g activity scores",
y = "Correlation gene expression and ArchR gene activity scores",
title = "SEACells") +
theme(legend.position = "None")

Distance weights
Using ArchR (Granja JM 2021) I computed peak-to-gene links across the entire chromosome, but not between chromsomes. This means that a lot of correlations are found between peaks very far away from the promoter/gene they are linked to. Even though these correlations can be quite high and interactions between enhancers and promoters can occur over megabase distances, a real biological interaction becomes less likely the larger the distance is. Therefore, since wer are interested in biologically relevant and not spurious correlations. Therefore, as suggested by (Granja JM 2021), I added distance weights, such that farther away peaks linked to a gene contribute less to the gene activity score of this particular gene.
Here, I used a distance decay from the TSS, computed as follows:
\(weight = e^{-(abs(distTSS/c))}\) with \(c\) being a constant determining the exponential decay rate of the distance weights. Below I tried different rates to better understand whether we can improve the gene activity scores by giving a higher weight to close peaks than to far away peaks. As can be seen below this did not improve, the scores, but rather the scores became worse, which is probably due to the fact that most correlation values will get very small weights this way and most peaks linked with a gene, even if the correlation value is high, will not contribute to the gene activity score anymore.
**Careful: The p2g inks in ArchR are computed for peak and gene pairs which are within a certain distance from each other. However, not the real TSS of a gene is used for this, but rater the distance between start coordinate of a gene and peak start coordinate, not taking into consideration the strand directionality.
!!!!!! Check again! Because here something is wrong with the way I compute the distance weights! Sometimes I need to use the start coordinate instead of the end coordinate. Try always using the gene start coordinate instead of swapping start and end coordinates in the dataframe. Maybe this is done automaticall when converted to dataframe?
Function to compute distance-weighted gene activity scores from p2g links
# As input for this function it is best to use only the most highly variable genes
distanc_weighted_gene_activity_scores <- function(p2g_mat_sub, geneModel = "exp(-distance/5000)",
weight = 50000,
peak_mat, links, p2g_original, gene_expr){
atac_granges <- metadata(p2g_original)[[1]]
#rna_granges <- metadata(p2g_original)[[2]]
gene_anno <- rowData(gene_expr)
# create gene annotations with start coordinate of each gene
# subset to contain only genes which are included in our peak2gene matrix
gene_anno <- gene_anno %>% as.data.frame() %>%
mutate(idxRNA = seq(nrow(.))) %>%
filter(name %in% rownames(p2g_mat_sub)) %>%
mutate(strand = ifelse(strand == 1, "+", "-")) %>%
mutate(start_coord = ifelse(strand == "+", start, end)) %>%
rename(gene = name) #%>% GRanges()
# subset atac granges & get middle of each peak
pos_atac_granges <- atac_granges %>%
as.data.frame() %>%
mutate(idxATAC = seq(nrow(.))) %>%
# group_by(seqnames) %>%
# mutate(idx = seq_along(seqnames)) %>%
# ungroup %>%
#tidyr::unite(chr_idx, seqnames, idx, remove = FALSE, sep = "_") %>%
filter(idxATAC %in% colnames(p2g_mat_sub)) %>%
mutate(middle = start + 300) #%>% GRanges()
#TODO: Filter for genes!
stopifnot(length(unique(links$idxATAC)) == dim(pos_atac_granges)[[1]])
stopifnot(length(unique(links$idxRNA)) == dim(gene_anno)[[1]])
#p2g_filt <- p2g_original %>% as.data.frame() %>% filter(gene %in% rownames(p2g_mat))
# combine the three dataframes
p2g_join <- left_join(links, as.data.frame(pos_atac_granges),
by = "idxATAC")
p2g_join <- left_join(p2g_join, as.data.frame(gene_anno),
by = "idxRNA", suffix = c(".atac", ".rna"))
# compute distance and distance weights
p2g_join <- p2g_join %>%
mutate(distance = abs(start_coord - middle)) %>%
mutate(distance_weight = eval(parse(text=geneModel)))
# create distance weight matrix
p2g_dw <- sparseMatrix(i = p2g_join$idxRNA,
j = p2g_join$idxATAC,
x = p2g_join$distance_weight,
dims = c(dim(assays(gene_expr)[[1]])[1],
dim(peak_mat)[1]),
dimnames = list(rowData(gene_expr)$name ,
seq.int(dim(peak_mat)[1])))
p2g_dw <- p2g_dw[as.vector(rownames(p2g_mat_sub)), colnames(p2g_mat_sub)]
# elementwise matrix multiplication
weighted_p2g_mat <- p2g_mat_sub * p2g_dw
print(paste(length(which(rowSums(weighted_p2g_mat) == 0)), "genes have only zero correlation values, so we will remove them."))
weighted_p2g_mat <- weighted_p2g_mat[rowSums(weighted_p2g_mat) != 0, ]
print(paste0("We are left with ", dim(weighted_p2g_mat)[1], " genes"))
# compute gene activity scores based on distance-weighted peak2gene matrix
weighted_scores <- gene_activity_scores(peak_mat_sub, weighted_p2g_mat)
return(weighted_scores)
}
weighted_scores <- distanc_weighted_gene_activity_scores(p2g_mat_sub, geneModel = "exp(-distance/5000)",
weight = 50000,
peak_mat, links, p2g, gene_expr)
KNN aggregates
rm(meta_rna)
#rm(p2g_mat)
gc(reset = TRUE)
## used (Mb) gc trigger (Mb) max used (Mb)
## Ncells 7263676 388.0 12756242 681.3 7263676 388.0
## Vcells 4199818138 32042.1 6153268697 46945.8 4199818138 32042.1
model_list <- c("exp(-abs(distance)/5000)", "exp(-abs(distance)/50000)",
"exp(-abs(distance)/500000)", "exp(-abs(distance)/5000000)")
# read in knn
rna_knn <- readRDS("11_added_Ricards_peaks_p2g_entire_chromosome/Peak2GeneLinks/seRNA-Group-KNN.rds")
cell_agg_list <- metadata(rna_knn)[[1]]
# aggregate for gene expression, ArchR gene activity scores and simple p2g links
rna_agg <- knn_aggregates(expr_mat_sub, cell_agg_list)
archr_knn <- archr_scores_mat[as.vector(rownames(rna_agg)),]
agg_archr_knn <- knn_aggregates(archr_knn, cell_agg_list)
agg_p2g_knn <- knn_aggregates(p2g_scores, cell_agg_list)
# compute rowwise correlations
archr_knn <- rowwise_correlations(rna_agg, agg_archr_knn, "Archr gene activity scores, KNN aggregates")
p2g_knn <- rowwise_correlations(rna_agg, agg_p2g_knn, "Peak-to-gene links activity scores, KNN aggregates")
cowplot::plot_grid(archr_knn[[2]], p2g_knn[[2]], ncol = 2)

# prepare lists to store correlation vectors and correlation histograms
corr_list <- list(archr_knn[[1]], p2g_knn[[1]])
# compute the distance-weighted gene activity scores from p2g links using different
# distance weight models
for (model in model_list){
weighted_scores <- distanc_weighted_gene_activity_scores(p2g_mat_sub,
geneModel = model,
weight = 50000,
peak_mat = peak_mat,
links = links,
p2g_original = p2g,
gene_expr = gene_expr)
agg_dist <- knn_aggregates(weighted_scores, cell_agg_list)
dist_knn <- rowwise_correlations(rna_agg, agg_dist, name = paste0("P2g activity scores, distance weihted, model = ", model))
stopifnot(any(is.na(dist_knn)) == FALSE)
corr_list <- append(corr_list, dist_knn[[1]])
print(dist_knn[[2]])
#corr_plots_list <- append(corr_plots_list, dist_knn[[2]])
plot <- ggplot() + #geom_density_2d_filled(aes(x = corr_list[[i]],
# y = corr_list[[1]]), alpha = .5) +
geom_point(aes(x = dist_knn[[1]], y = corr_list[[1]])) +
geom_line(aes(x = dist_knn[[1]], y = dist_knn[[1]]), col = "red") +
theme(legend.position = "None") +
labs(x = "Correlation gene expression & p2g activity scores",
title = paste0(model, "KNN aggregates"),
y = "Correlation gene expression & ArchR gene activity scores")
print(plot)
}
## [1] "0 genes have only zero correlation values, so we will remove them."
## [1] "We are left with 1285 genes"
## [1] "normalized the p2g matrix"
## [1] "Computed weightes sum of peaks for each gene and cell"
## [1] "Normalized for library size"
## [1] "Exponentiated matrix"
## [1] "Divided by total activity to get value between zero and one"


## [1] "0 genes have only zero correlation values, so we will remove them."
## [1] "We are left with 1285 genes"
## [1] "normalized the p2g matrix"
## [1] "Computed weightes sum of peaks for each gene and cell"
## [1] "Normalized for library size"
## [1] "Exponentiated matrix"
## [1] "Divided by total activity to get value between zero and one"


## [1] "0 genes have only zero correlation values, so we will remove them."
## [1] "We are left with 1285 genes"
## [1] "normalized the p2g matrix"
## [1] "Computed weightes sum of peaks for each gene and cell"
## [1] "Normalized for library size"
## [1] "Exponentiated matrix"
## [1] "Divided by total activity to get value between zero and one"


## [1] "0 genes have only zero correlation values, so we will remove them."
## [1] "We are left with 1285 genes"
## [1] "normalized the p2g matrix"
## [1] "Computed weightes sum of peaks for each gene and cell"
## [1] "Normalized for library size"
## [1] "Exponentiated matrix"
## [1] "Divided by total activity to get value between zero and one"


SEAcells
# prepare lists to store correlation vectors and correlation histograms
corr_list <- list(seacell_corr_archr[[1]], seacell_corr_p2g[[1]])
# compute the distance-weighted gene activity scores from p2g links using different
# distance weight models
for (model in model_list){
weighted_scores <- distanc_weighted_gene_activity_scores(p2g_mat_sub,
geneModel = model,
weight = 50000,
peak_mat = peak_mat,
links = links,
p2g_original = p2g,
gene_expr = gene_expr)
agg_dist <- create_seacell_aggregates(weighted_scores, seacells)
dist_knn <- rowwise_correlations(seacell_rna_agg, agg_dist, name = paste0("P2g activity scores, distance weighted, model = ", model))
stopifnot(any(is.na(dist_knn)) == FALSE)
corr_list <- append(corr_list, dist_knn[[1]])
print(dist_knn[[2]])
#corr_plots_list <- append(corr_plots_list, dist_knn[[2]])
plot <- ggplot() + #geom_density_2d_filled(aes(x = corr_list[[i]],
# y = corr_list[[1]]), alpha = .5) +
geom_point(aes(x = dist_knn[[1]], y = corr_list[[1]])) +
geom_density_2d_filled(aes(x = dist_knn[[1]],
y = corr_list[[1]]), alpha = 0.5) +
geom_line(aes(x = corr_list[[1]], y = corr_list[[1]]), col = "red") +
theme(legend.position = "None") +
labs(x = "Correlation gene expr. & p2g activity scores",
title = paste0(model, "SEACells"),
y = "Correlation gene expr. & ArchR gene activity scores")
print(plot)
}
## [1] "0 genes have only zero correlation values, so we will remove them."
## [1] "We are left with 1285 genes"
## [1] "normalized the p2g matrix"
## [1] "Computed weightes sum of peaks for each gene and cell"
## [1] "Normalized for library size"
## [1] "Exponentiated matrix"
## [1] "Divided by total activity to get value between zero and one"


## [1] "0 genes have only zero correlation values, so we will remove them."
## [1] "We are left with 1285 genes"
## [1] "normalized the p2g matrix"
## [1] "Computed weightes sum of peaks for each gene and cell"
## [1] "Normalized for library size"
## [1] "Exponentiated matrix"
## [1] "Divided by total activity to get value between zero and one"


## [1] "0 genes have only zero correlation values, so we will remove them."
## [1] "We are left with 1285 genes"
## [1] "normalized the p2g matrix"
## [1] "Computed weightes sum of peaks for each gene and cell"
## [1] "Normalized for library size"
## [1] "Exponentiated matrix"
## [1] "Divided by total activity to get value between zero and one"


## [1] "0 genes have only zero correlation values, so we will remove them."
## [1] "We are left with 1285 genes"
## [1] "normalized the p2g matrix"
## [1] "Computed weightes sum of peaks for each gene and cell"
## [1] "Normalized for library size"
## [1] "Exponentiated matrix"
## [1] "Divided by total activity to get value between zero and one"


Gene window, no distance weights
There are two options when defining the gene window. One option is to extend +/- 100bp up- and downstream of the TSS. However, since genes have different sizes, some gene bodies might be much larger than these gene windows. The second option is to extend the gene window not from the TSS, but from the start and end corrdinate of the gene body respectively. This way, more peaks will be taken into consideration if a gene is larger, simply because the gene window will be larger. Therefore, in ArchR they use an additional weight for the gene body size to account for this effect. Here, we extend the gene window around the TSS. As can be seen in the plot below, this does not yield better results, probably, because we are removing a lot of correlations which are high and, therefore, important for the prediction.
This is not what would be expected, since some high correlations within the gene window are very likely to be biologically important and should recapitulate gene expression quite well. This is also shown by the ArchR gene activity scores, which use gene window as well to restric the influence of accessible regions to a certain window around the gene’s TSS. One reason could be that the peak-to-gene links identified by simple correlations are not biologically meaningful, therefore also very far away correlations are important for recapitulating gene expression.
Gene window around TSS
# As input for this function it is best to use only the most highly variable genes
compute_gene_window_score <- function(p2g_mat_sub, peak_mat, links, p2g_original, gene_expr){
# create gene annotations with start coordinate of each gene
# subset to contain only genes which are included in our peak2gene matrix
gene_anno <- rowData(gene_expr) %>%
as.data.frame() %>%
mutate(idxRNA = seq(nrow(.))) %>%
filter(name %in% rownames(p2g_mat_sub)) %>%
mutate(strand = ifelse(strand == 1, "+", "-")) %>%
mutate(start_coord = ifelse(strand == "+", start, end)) %>%
rename(gene = name) #%>% GRanges()
# extend gene regions +/- 100bp up- and downstream of the TSS
gene_regions <- resize(gene_anno %>% GRanges(), width = 1)
extendedGeneRegion <- (suppressWarnings(extendGR(gene_regions,
upstream = 100000,
downstream = 100000)))
# subset atac granges & get middle of each peak
pos_atac_granges <- metadata(p2g_original)[[1]] %>%
as.data.frame() %>%
mutate(idxATAC = seq(nrow(.))) %>%
# group_by(seqnames) %>%
# mutate(idx = seq_along(seqnames)) %>%
# ungroup %>%
#tidyr::unite(chr_idx, seqnames, idx, remove = FALSE, sep = "_") %>%
filter(idxATAC %in% colnames(p2g_mat_sub)) %>%
mutate(middle = start + 300) #%>% GRanges()
#TODO: Filter for genes!
stopifnot(length(unique(links$idxATAC)) == dim(pos_atac_granges)[[1]])
stopifnot(length(unique(links$idxRNA)) == dim(gene_anno)[[1]])
#p2g_filt <- p2g_original %>% as.data.frame() %>% filter(gene %in% rownames(p2g_mat))
# find overlapping peaks and gene window in chromosome-aware fashion
tmp <- suppressWarnings(findOverlaps(extendedGeneRegion, pos_atac_granges %>% GRanges()))
print(paste0("Out of ", subjectLength(tmp), " peaks only ",
length(unique(subjectHits(tmp))), " peaks are found within gene window of 200kb."))
### some plots
p1 <- (tmp %>% as.data.frame() %>%
group_by(queryHits) %>% # gene region
summarize(n = n()) %>% # get number of peaks overlapping with a gene region
ggplot() + geom_histogram(aes(x = n), bins = 100, fill="#69b3a2") +
labs(title = "number of peaks per gene region of size +/- 100kb from TSS",
x = "number of peaks within window"))
# combine the three dataframes
p2g_join <- left_join(links, as.data.frame(pos_atac_granges),
by = "idxATAC")
p2g_join <- left_join(p2g_join, as.data.frame(gene_anno),
by = "idxRNA", suffix = c(".atac", ".rna"))
# compute distance and distance weights
p2g_join <- p2g_join %>%
mutate(distance = abs(start_coord - middle))# %>%
# mutate(distance_weight = eval(parse(text=geneModel)))
p2 <- p2g_join %>% ggplot() +
geom_histogram(aes(x = distance), bins = 100) +
labs(title = "Distance", x = "distance") +
geom_vline(xintercept = 100000, color = "red")
# p2 <- p2g_join %>% ggplot() +
# geom_histogram(aes(x = (distance_weight)), bins = 100) +
# scale_y_log10() +
# labs(title = "Distance Weights", x = "distance weights")
print(cowplot::plot_grid(p1, p2, ncol = 2))#), ncol = 2))
# create a dataframe of all peaks which overlap their corresponding gene window
peaks_in_gene_window <- data.frame(gene = gene_regions[queryHits(tmp)]$gene,
peak = (pos_atac_granges %>% GRanges())[subjectHits(tmp)]$idxATAC) %>%
unite(peak_gene_window, gene, peak, sep = "#", remove = FALSE)
# filter the p2g link dataframe for only peaks which are within a gene window
corr_window <- p2g_join %>%
unite(peak_gene_window, gene, idxATAC, sep = "#", remove = FALSE) %>%
filter(peak_gene_window %in% peaks_in_gene_window$peak_gene_window)
### PLOTS
p1 <- corr_window %>%
ggplot() +
geom_histogram(aes(x = Correlation), bins = 200, fill = "#69b3a2") +
labs(title = "Correlation values of peaks found within gene windows")
p2 <- corr_window %>%
ggplot() +
geom_histogram(aes(x = distance), bins = 200, fill = "#69b3a2") +
labs(title = "Distance between peaks and genes found within gene windows and TSS")
p3 <- corr_window %>%
mutate(bin = cut_width(distance, width=10000, boundary=0)) %>%
ggplot() +
geom_boxplot(aes(x = bin, y = Correlation), fill = "#69b3a2") +
labs(title = "Distance and Correlation within gene window, 1000bp bins",
x = "Distance (1000bp bins)") +
scale_x_discrete(guide = guide_axis(angle = 90))
print(cowplot::plot_grid(p1, p2, p3, ncol = 1))
p1 <- ggplot() +
geom_histogram(aes(x = rowSums(p2g_mat_sub > 0)), bins = 200, fill = "#69b3a2") +
scale_y_log10() +
labs(title = "# peaks correlated with each gene",
x = "number of peaks", y = "log10(count)")
p2 <- ggplot() +
geom_histogram(aes(x = colSums(p2g_mat_sub > 0)), bins = 70, fill = "#69b3a2") +
scale_y_log10() +
labs(title = "# genes correlated with each peak",
y = "log10(count)", x = "number of genes")
p3 <- ggplot() +
geom_histogram(aes(x = rowSums(p2g_mat_sub > 0)), bins = 200, fill = "#69b3a2") +
labs(title = "# peaks correlated with each gene",
x = "number of peaks", y = "count")
p4 <- ggplot() +
geom_histogram(aes(x = colSums(p2g_mat_sub > 0)), bins = 70, fill = "#69b3a2") +
labs(title = "# genes correlated with each peak",
y = "count", x = "number of genes")
print(cowplot::plot_grid(p1, p2, p3, p4, ncol = 2))
#
#
# peak_middle_region <- pos_atac_granges %>% GRanges()
# # add the half width to the start of each peak
# start(peak_middle_region) = start(peak_middle_region) +
# floor(width(peak_middle_region) / 2)
# # resize the ranges so we only have the middle of each peak
# peak_middle_region <- resize(peak_middle_region, 1, "start")
#
# # compute the distances between peak middle and gene TSS of all peaks which
# # overlap with a gene window
# distance <- distance(ranges(gene_regions)[queryHits(tmp)],
# ranges(resize(peak_middle_region, width = 1))[subjectHits(tmp)])
#
#
# ### PLOT
# # p1 <- ggplot() + geom_histogram(aes(x = distance), bins = 200) +
# # scale_y_log10() +
# # labs(title = "Distance between peak middle and gene TSS within a gene window",
# # y = "log10(count)") +
# # geom_vline(xintercept = 100000, color = "red")
#
#
#
# isMinus <- BiocGenerics::which(strand(gene_regions) == "-")
# # subtract the gene start coordinate from the tile start coordinate -> relative distances
# signDist <- sign(start(peak_middle_region)[subjectHits(tmp)] -
# start(resize(gene_regions,1,"start"))[queryHits(tmp)])
# # convert the direction of distance for all distances corresponding to the negative strand
# signDist[isMinus] <- signDist[isMinus] * -1
#
#
# distance <- distance * signDist
#
#
#
# #### PLOT
# p2 <- ggplot() + geom_histogram(aes(x = distance), bins = 500) +
# scale_y_log10() +
# labs(title = "Relative distance of peaks to TSS within a gene window",
# x = "Relative distance to TSS", y = "log10(count)") +
# geom_vline(xintercept = c(100000, -100000), color = "red")
#
# print(p2)
# #cowplot::plot_grid(p1, p2, ncol = 1)
p2g_links_gene_window <- Matrix::sparseMatrix(
i = corr_window$idxRNA,
j = corr_window$idxATAC,
x = corr_window$Correlation,
dims = c(nrow(expr_mat), nrow(peak_mat)),
dimnames = list(rownames(expr_mat),rownames(peak_mat))
)
print(paste0("The peak-to-gene links matrix, restricted to a +/- 100kb window around the TSS has dimensions ", split(dim(p2g_links_gene_window), 1)))
print(paste0("The maximum value is: ", max(p2g_links_gene_window), ", the minum value is: ", min(p2g_links_gene_window) ))
p2g_links_gene_window <- p2g_links_gene_window[rowSums(p2g_links_gene_window) != 0, ]
p2g_links_gene_window <- p2g_links_gene_window[, colSums(p2g_links_gene_window) != 0]
print(paste0("After removing any rows and columsn which do not contain any links we are left with ", nrow(p2g_links_gene_window), " genes and ", ncol(p2g_links_gene_window), " peaks."))
# Compute gene activity scores
gene_window_scores <- gene_activity_scores(peak_mat_sub[colnames(p2g_links_gene_window), ], p2g_links_gene_window)
dim(gene_window_scores)
return(gene_window_scores)
}
gene_window_scores <- compute_gene_window_score(
p2g_mat_sub = p2g_mat_sub,
peak_mat = peak_mat,
links = links,
p2g_original = p2g,
gene_expr = gene_expr)
## [1] "Out of 133422 peaks only 22749 peaks are found within gene window of 200kb."

## Warning: Transformation introduced infinite values in continuous y-axis
## Warning: Removed 31 rows containing missing values (geom_bar).
## Warning: Transformation introduced infinite values in continuous y-axis
## Warning: Removed 5 rows containing missing values (geom_bar).


## [1] "The peak-to-gene links matrix, restricted to a +/- 100kb window around the TSS has dimensions c(16701, 180499)"
## [1] "The maximum value is: 0.939676699453905, the minum value is: 0"
## [1] "After removing any rows and columsn which do not contain any links we are left with 1124 genes and 5980 peaks."
## [1] "normalized the p2g matrix"
## [1] "Computed weightes sum of peaks for each gene and cell"
## [1] "Normalized for library size"
## [1] "Exponentiated matrix"
## [1] "Divided by total activity to get value between zero and one"
KNN aggregates
First, I compared the distance weigthed gene activity scores based on the ArchR KNN aggregates.
weighted_scores_agg <- knn_aggregates(gene_window_scores, cell_agg_list)
weighted_knn_corr <- rowwise_correlations(rna_agg, weighted_scores_agg,
"P2g links within gene window")
weighted_knn_corr[[2]]

ggplot() +
geom_density_2d_filled(aes(x = weighted_knn_corr[[1]],
y = archr_knn[[1]][names(weighted_knn_corr[[1]])]),
alpha = .5) +
geom_point(aes(x = weighted_knn_corr[[1]], y = archr_knn[[1]][names(weighted_knn_corr[[1]])])) +
geom_line(aes(x = weighted_knn_corr[[1]], y = weighted_knn_corr[[1]]), col = "red") +
theme(legend.position = "None") +
labs(x = "Correlation between gene expression and p2g activity scores",
title = "Peak-to-gene links are restricted to a gene window of +/- 100kb around TSS",
y = "Correlation between gene expression and ArchR gene activity scores")

SEACells
Second, I compared the distance weigths using the SEACell aggregates, which yields better results as can be seen above.
gene_window_agg <- create_seacell_aggregates(gene_window_scores, seacells)
gene_window_corr <- rowwise_correlations(seacell_rna_agg, gene_window_agg,
name = "Gene window around TSS")
gene_window_corr[[2]]

ggplot() +
geom_point(aes(x = gene_window_corr[[1]],
y = seacell_corr_archr[[1]][names(gene_window_corr[[1]])])) +
geom_density_2d_filled(aes(
y = seacell_corr_archr[[1]][names(gene_window_corr[[1]])],
x = gene_window_corr[[1]]), alpha = 0.5) +
geom_line(aes(x = seacell_corr_archr[[1]][names(gene_window_corr[[1]])],
y = seacell_corr_archr[[1]][names(gene_window_corr[[1]])]),
color = "red") +
theme(legend.position = "None") +
labs(x = "Correlation between gene expression and p2g activity scores, gene window",
y = "Correlation between gene expression and ArchR gene activity scores",
title = "Peak-to-gene links within gene window")

Effect of using different distance decay rates
How does the distance weight distribution change with different decay rates?
Here, we use the formula \(e^{\frac{-abs(distance)}{c}}\) with differen decay rates \(c \in \{5000, 50000, 500000, 5000000\}\). Additionally, we use only peaks which overlap with a +/- 100kb window from the TSS.
model_list <- c("exp(-abs(distance)/5000)", "exp(-abs(distance)/50000)",
"exp(-abs(distance)/500000)", "exp(-abs(distance)/5000000)")
atac_granges <- metadata(p2g)[[1]]
#rna_granges <- metadata(p2g_original)[[2]]
gene_anno <- rowData(gene_expr)
# create gene annotations with start coordinate of each gene
# subset to contain only genes which are included in our peak2gene matrix
gene_anno <- gene_anno %>% as.data.frame() %>%
mutate(idxRNA = seq(nrow(.))) %>%
filter(name %in% rownames(p2g_mat_sub)) %>%
mutate(strand = ifelse(strand == 1, "+", "-")) %>%
mutate(start_coord = ifelse(strand == "+", start, end)) %>%
rename(gene = name) #%>% GRanges()
# subset atac granges & get middle of each peak
pos_atac_granges <- atac_granges %>%
as.data.frame() %>%
mutate(idxATAC = seq(nrow(.))) %>%
# group_by(seqnames) %>%
# mutate(idx = seq_along(seqnames)) %>%
# ungroup %>%
#tidyr::unite(chr_idx, seqnames, idx, remove = FALSE, sep = "_") %>%
filter(idxATAC %in% colnames(p2g_mat_sub)) %>%
mutate(middle = start + 300) #%>% GRanges()
# combine the three dataframes
p2g_join <- left_join(links, as.data.frame(pos_atac_granges),
by = "idxATAC")
p2g_join <- left_join(p2g_join, as.data.frame(gene_anno),
by = "idxRNA", suffix = c(".atac", ".rna"))
# compute distance and distance weights
p2g_join <- p2g_join %>%
mutate(distance = abs(start_coord - middle)) %>%
mutate(rel_distance = start_coord - middle)
# mutate(distance_weight = eval(parse(text=geneModel)))
for (model in model_list){
# compute distance and distance weights
p2g_join <- p2g_join %>%
mutate(distance = abs(start_coord - middle)) %>%
mutate(distance_weight = eval(parse(text=model)))
p1 <- p2g_join %>% ggplot() +
geom_histogram(aes(x = distance), bins = 200, fill="#69b3a2") +
labs(title = "Distance between peaks and genes", x = "distance") +
geom_vline(xintercept = 5000, color = "red") +
geom_vline(xintercept = 250000, color = "orange")
p2 <- p2g_join %>% ggplot() +
geom_histogram(aes(x = (distance_weight)), bins = 200, fill="#69b3a2") +
scale_y_log10() +
labs(title = paste0("Distance_decay: ", model),
x = "distance weights", y = "log10(counts)")
print(cowplot::plot_grid(p1, p2, ncol = 2))
}
## Warning: Transformation introduced infinite values in continuous y-axis
## Warning: Removed 2 rows containing missing values (geom_bar).




# Relationship between distance and correlation value
# p3 <- p2g_join %>% ggplot() +
# geom_point(aes(x = Correlation, y = distance)) +
# labs(title = "Distance vs. correlation between peaks and genes",
# x = "Correlation between peak and gene",
# y = "Distance between peak and gene")
#
#
# p4 <- p2g_join %>% ggplot() +
# geom_point(aes(x = Correlation, y = distance_weight)) +
# labs(title = "Distance vs. correlation between peaks and genes",
# x = "Correlation between peak and gene",
# y = "Distance weights between peak and gene")
#cowplot::plot_grid(p1, p2, ncol = 1)
Relationship between distance and correlation values
# Olot relationship between distance and correlation as density plots
p1 <- p2g_join %>% ggplot() +
geom_density_2d_filled(aes(x = Correlation, y = distance)) +
theme(legend.position = "None") +
labs(title = "Relationship between distance and correlation")
p2 <- p2g_join %>%
filter(Correlation > 0.3) %>%
ggplot() +
geom_density_2d_filled(aes(x = Correlation, y = distance)) +
theme(legend.position = "None") +
labs(title = "Relationship between distance and correlation")
p3 <- p2g_join %>%
filter(Correlation > 0.6) %>%
ggplot() +
geom_density_2d_filled(aes(x = Correlation, y = distance)) +
theme(legend.position = "None") +
labs(title = "Relationship between distance and correlation")
cowplot::plot_grid(p1, p2, p3, ncol = 2)
p2g %>%
mutate(bin=cut_width(distance, width=100000, boundary=0)) %>%
filter(distance < 10000000) %>%
ggplot() +
geom_boxplot(aes(x = bin, y = Correlation), fill="#69b3a2") +
#geom_vline(xintercept = 250000, color = "red") +
labs(title = "Relationship between distance and correlation of p2g links, 100kb bins",
x = "Distance between peaks and genes within 250kb", y = "Correlation") +
theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust=1))
p2g_join %>%
mutate(bin=cut_width(distance, width=100000, boundary=0)) %>%
filter(distance < 10000000) %>%
ggplot() +
geom_boxplot(aes(x = bin, y = Correlation), fill="#69b3a2") +
#geom_vline(xintercept = 250000, color = "red") +
labs(title = "Relationship between distance and correlation of p2g links, 100kb bins",
x = "Distance between peaks and genes within 250kb", y = "Correlation") +
theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust=1))

p1 <- p2g_join %>%
mutate(bin=cut_width(distance, width=100000, boundary=0)) %>%
filter(distance < 10000000 & Correlation > 0.5) %>%
ggplot() +
geom_boxplot(aes(x = bin, y = Correlation), fill="#69b3a2") +
labs(title = "Relationship between distance and correlation of p2g links, 100kb bins",
x = "Distance < 1e^7 bp", y = "Correlation > 0.5") +
theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust=1))
p2 <- p2g_join %>%
mutate(bin=cut_width(distance, width=100000, boundary=0)) %>%
filter(distance < 10000000 & Correlation > 0.8) %>%
ggplot() +
geom_boxplot(aes(x = bin, y = Correlation), fill="#69b3a2") +
labs(title = "Relationship between distance and correlation of p2g links, 100kb bins",
x = "Distance < 1e^7 bp", y = "Correlation > 0.8") +
theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust=1))
p3 <- p2g_join %>%
mutate(bin=cut_width(distance, width=100000, boundary=0)) %>%
filter(distance < 10000000 & Correlation < 0.5) %>%
ggplot() +
geom_boxplot(aes(x = bin, y = Correlation), fill="#69b3a2") +
labs(title = "Relationship between distance and correlation of p2g links, 100kb bins",
x = "Distance < 1e^7 bp", y = "Correlation < 0.5") + theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust=1))
p4 <- p2g_join %>%
mutate(bin=cut_width(distance, width=1000, boundary=0)) %>%
filter(distance < 100000 & Correlation > 0.5) %>%
ggplot() +
geom_boxplot(aes(x = bin, y = Correlation), fill="#69b3a2") +
labs(title = "Relationship between distance and correlation of p2g links, 1kb bins",
x = "Distance < 100kb", y = "Correlation > 0.5") + theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust=1))
cowplot::plot_grid(p1, p2, p3, p4, ncol = 2)

Lets have a look at correlation values between peaks within the promoter region of a TSS, namely 5kb upstream of the TSS.
p2g_join %>%
mutate(bin=cut_width(rel_distance, width=100, boundary=0)) %>%
filter(rel_distance < 0 & rel_distance >= -5000) %>%
ggplot() +
geom_boxplot(aes(x = bin, y = Correlation), fill="#69b3a2") +
#geom_vline(xintercept = 250000, color = "red") +
labs(title = "Distance -5kb upstream of TSS, 100bp bins",
x = "Distance -5kb upstream of TSS", y = "Correlation") +
theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust=1))

Try distance decay rates
rna_knn <- readRDS("11_added_Ricards_peaks_p2g_entire_chromosome/Peak2GeneLinks/seRNA-Group-KNN.rds")
#rna_agg_mat <- assays(rna_knn)[[1]]
#rownames(rna_agg_mat) <- rowData(rna_knn)$name
cell_agg_list <- metadata(rna_knn)[[1]]
knn_aggregates <- function(matrix, cell_agg_list){
# empty matrix to store aggregates
agg <- matrix(data = 0,
nrow = dim(matrix)[1],
ncol = length(cell_agg_list),
dimnames = list(rownames(matrix), NULL))
for (i in seq.int(length(cell_agg_list))) {
agg[, i] <- rowSums(matrix[, cell_agg_list[[i]]])
}
return(agg)
}
rna_agg <- knn_aggregates(expr_mat_sub, cell_agg_list)
archr_knn <- archr_scores_mat[as.vector(rownames(agg_p2g_knn)),]
agg_archr_knn <- knn_aggregates(archr_knn, cell_agg_list)
agg_p2g_knn <- knn_aggregates(p2g_scores, cell_agg_list)
agg_dist <- knn_aggregates(weighted_scores, cell_agg_list)
archr_knn <- rowwise_correlations(rna_agg, agg_archr_knn, "Archr gene activity scores")
p2g_knn <- rowwise_correlations(rna_agg, agg_p2g_knn, "Peak-to-gene links activity scores")
dist_knn <- rowwise_correlations(rna_agg, agg_dist, "Peak-to_gene links activity scores weighted by distance")
cowplot::plot_grid(archr_knn[[2]], p2g_knn[[2]], dist_knn[[2]], ncol = 2)

p1 <- ggplot() + geom_density_2d_filled(aes(x = p2g_knn[[1]],
y = archr_knn[[1]]), alpha = .5) +
geom_point(aes(x = p2g_knn[[1]], y = archr_knn[[1]])) +
geom_line(aes(x = p2g_knn[[1]], y = p2g_knn[[1]]), col = "red") +
theme(legend.position = "None")
p2 <- ggplot() + geom_density_2d_filled(aes(x = dist_knn[[1]],
y = archr_knn[[1]]), alpha = .5) +
geom_point(aes(x = dist_knn[[1]], y = archr_knn[[1]])) +
geom_line(aes(x = dist_knn[[1]], y = dist_knn[[1]]), col = "red") +
theme(legend.position = "None")
cowplot::plot_grid(p1, p2, ncol = 2)

TAD boundaries
In case Hi-C data are available, TAD boundaries could aid in finding peak-to-gene links. Setting a distance decay rate to the same value for all genes and celltypes, does not give credit to the biological variability associated with gene regulation. In (Zuin J. 2022) it has been shown experimentally, that interactions between regulatory elements decay exponentially within TAD boundaries and almost disappear completely beyond TAD boundaries. Here, I restricted the peak-to-gene links identified by ArchR to within TAD boundaries and computed gene activity scores again.
tad_boundaries <- as.data.frame(read.table("jupyter_notebooks/tad_e75.bed", header = FALSE, sep = "\t", stringsAsFactors = FALSE, quote = ""))
tad_boundaries <- tad_boundaries %>%
rename(seqnames = V1, start = V2, end = V3) %>%
GRanges()
p1 <- ggplot() + geom_histogram(aes(x = width(gene_anno %>% GRanges())),
bins = 200) +
geom_vline(xintercept = median(width(gene_anno %>% GRanges())),
color = "orange") +
labs(title = paste0("Distribution of gene size, median size = ",
median(width(gene_anno %>% GRanges()))),
x = "Gene size in bp")
p2 <- ggplot() + geom_histogram(aes(x = width(tad_boundaries)), bins = 200) +
geom_vline(xintercept = median(width(tad_boundaries)), color = "orange") +
labs(title = paste0("Distribution of TAD boundary size, median size = ",
median(width(tad_boundaries))),
x = "TAD boundary size in bp")
cowplot::plot_grid(p1, p2, ncol = 1)

What is the distribution of peaks and genes within TAD boundaries?
gene_anno <- rowData(gene_expr)
# create gene annotations with start coordinate of each gene
# subset to contain only genes which are included in our peak2gene matrix
gene_anno <- gene_anno %>% as.data.frame() %>%
mutate(idxRNA = seq(nrow(.))) %>%
filter(name %in% rownames(p2g_mat_sub)) %>%
mutate(strand = ifelse(strand == 1, "+", "-")) %>%
mutate(start_coord = ifelse(strand == "+", start, end)) %>%
rename(gene = name) #%>% GRanges()
# subset atac granges & get middle of each peak
pos_atac_granges <- atac_granges %>%
as.data.frame() %>%
mutate(idxATAC = seq(nrow(.))) %>%
# group_by(seqnames) %>%
# mutate(idx = seq_along(seqnames)) %>%
# ungroup %>%
#tidyr::unite(chr_idx, seqnames, idx, remove = FALSE, sep = "_") %>%
filter(idxATAC %in% colnames(p2g_mat_sub)) %>%
mutate(middle = start + 300) #%>% GRanges()
#TODO: Filter for genes!
stopifnot(length(unique(links$idxATAC)) == dim(pos_atac_granges)[[1]])
stopifnot(length(unique(links$idxRNA)) == dim(gene_anno)[[1]])
#p2g_filt <- p2g_original %>% as.data.frame() %>% filter(gene %in% rownames(p2g_mat))
# find overlapping peaks and gene window in chromosome-aware fashion
tad_overlaps_genes <- suppressWarnings(findOverlaps(gene_anno %>% GRanges(),
tad_boundaries))
p1 <- tad_overlaps_genes %>% as.data.frame() %>%
group_by(subjectHits) %>%
summarise(n = n()) %>%
ggplot() + geom_histogram(aes(x = n), bins = 100) +
labs(title = "Number of highly variable genes within a tad boundary",
x = "number of genes/tad boundary")
tad_overlaps_peaks <- suppressWarnings(findOverlaps(pos_atac_granges %>% GRanges(),
tad_boundaries))
p2 <- tad_overlaps_peaks %>% as.data.frame() %>%
group_by(subjectHits) %>%
summarise(n = n()) %>%
ggplot() + geom_histogram(aes(x = n), bins = 100) +
labs(title = "Number of peaks within a tad boundary",
x = "number of peaks/tad boundary")
cowplot::plot_grid(p1, p2, ncol = 2)

How many p2g links are within tad boundaries?
Peak-to-gene links considered in above computations
All peak-to-gene links
```#{r, fig.width=12, fig.height=5} rm(peaks) gc(reset = TRUE)
p2g_pos <- p2g %>% as.data.frame() %>% filter(Correlation > 0) %>% unite(link, idxRNA, idxATAC, sep = “%”, remove = FALSE)
gene_anno_all <- rowData(gene_expr) %>% as.data.frame() %>% mutate(idxRNA = seq(nrow(.))) %>% filter(idxRNA %in% p2g_pos$idxRNA) %>% mutate(strand = ifelse(strand == 1, “+”, “-”)) %>% mutate(start_coord = ifelse(strand == “+”, start, end)) %>% rename(gene = name) #%>% GRanges()
subset atac granges & get middle of each peak
pos_atac_granges_all <- metadata(p2g)[[1]] %>% as.data.frame() %>% mutate(idxATAC = seq(nrow(.))) %>% # group_by(seqnames) %>% # mutate(idx = seq_along(seqnames)) %>% # ungroup %>% #tidyr::unite(chr_idx, seqnames, idx, remove = FALSE, sep = "_") %>% filter(idxATAC %in% p2g_pos$idxATAC) %>% mutate(middle = start + 300) #%>% GRanges()
combine the three dataframes
p2g_join_all <- left_join(p2g_pos, as.data.frame(pos_atac_granges_all), by = “idxATAC”) p2g_join_all <- left_join(p2g_join_all, as.data.frame(gene_anno_all), by = “idxRNA”, suffix = c(“.atac”, “.rna”))
p2g_join_all <- p2g_join_all %>% mutate(distance = abs(start_coord - middle))
# find overlapping peaks and gene window in chromosome-aware fashion tad_overlaps_genes <- suppressWarnings(findOverlaps(gene_anno_all %>% GRanges(), tad_boundaries))
get genes which are not found within two TAD boundaries, but only within one
within_genes <- (tad_overlaps_genes %>% as.data.frame() %>% group_by(queryHits) %>% summarise(n = n()) %>% ungroup() %>% filter(n < 2))$queryHits
print(paste0(“Out of”, nrow(gene_anno_all), " genes, “, length(unique(queryHits(tad_overlaps_genes))),” genes are within TAD boundaries. Some of these genes even span across TAD boudnaries, namely “, abs(length(within_genes) - length(unique(queryHits(tad_overlaps_genes)))),”."))
We only keep genes within boundaries, but not genes crossing boundaries
tad_overlaps_genes <- tad_overlaps_genes %>% as.data.frame %>% filter(queryHits %in% within_genes) #%>% S4Vectors::as()
get peaks overlapping with tad boundaries
tad_overlaps_peaks <- suppressWarnings(findOverlaps(pos_atac_granges_all %>% GRanges(), tad_boundaries))
filter for peaks overlapping tad boundaries which also contain genes
tad_overlaps_peaks <- tad_overlaps_peaks %>% as.data.frame() %>% filter(subjectHits %in% tad_overlaps_genes$subjectHits)
combine tad boundaries which contain genes and peaks
tad_combine <- left_join(tad_overlaps_genes, tad_overlaps_peaks, copy = TRUE, by = “subjectHits”, suffix = c(“.gene”, “.peak”)) %>% unite(link, queryHits.gene, queryHits.peak, sep = “%”, remove = FALSE)
genes <- gene_anno_all[tad_combine$queryHits.gene, ] %>% mutate(tad_index = tad_combine$subjectHits)
peak_coll <- pos_atac_granges_all[tad_combine$queryHits.peak, ] %>% mutate(tad_index = tad_combine$subjectHits)
gene_peak_tad_df <- left_join(genes, peak_coll, by = “tad_index”, suffic = c(“.gene”, “.peak”)) %>% unite(peak_gene_tad, gene, idxATAC, sep = “#”, remove = FALSE)
some plots
p1 <- (tad_overlaps_peaks %>% as.data.frame() %>% group_by(subjectHits) %>% # gene region summarize(n = n()) %>% # get number of peaks overlapping with a gene region ggplot() + geom_histogram(aes(x = n), bins = 100, fill=“#69b3a2”) + labs(title = “Number of peaks per tad boundary, positive p2g links”, x = “number of peaks”))
p2 <- (tad_overlaps_genes %>% as.data.frame() %>% group_by(subjectHits) %>% # gene region summarize(n = n()) %>% # get number of peaks overlapping with a gene region ggplot() + geom_histogram(aes(x = n), bins = 100, fill=“#69b3a2”) + labs(title = “Number of genes per tad boundary, positive p2g links”, x = “number of genes”))
print(cowplot::plot_grid(p1, p2, ncol = 2))
print(paste0(“The number of positive peak-to-gene links is:”, length(p2g_pos\(link))) print(paste0( "The number of positive peak-to-gene links within TAD boundaries is: ", length(tad_combine\)link)))
print(paste0(“The number of positive peak-to-gene links outside TAD boundaries is:”, length(p2g_pos\(link) - length(tad_combine\)link)))
print(paste0(“The proportion of peak-to-gene links within TAD boundaries out of all positive peak-to-gene links across the entire chromosome is”, round(length(tad_combine\(link) / length(p2g_pos\)link), 5)))
#ggplot() + geom_point(aes(x = p2g_pos\(idxATAC, y = p2g_pos\)idxRNA))
```
Distance vs. Correlation
Here I visualize the relationship between distance between peaks and genes and their respective correlation values using all positive links obtained using ArchR
p2g_join_all %>%
mutate(bin=cut_width(distance, width=100000, boundary=0)) %>%
filter(distance < 10000000) %>%
ggplot() +
geom_boxplot(aes(x = bin, y = Correlation), fill="#69b3a2") +
#geom_vline(xintercept = 250000, color = "red") +
labs(title = "Relationship between distance and correlation of p2g links, 100kb bins",
x = "Distance between peaks and genes within 250kb", y = "Correlation") +
theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust=1))
colPalette_celltypes = c('#532C8A',
'#c19f70',
'#f9decf',
'#c9a997',
'#B51D8D',
'#3F84AA',
'#9e6762',
'#354E23',
'#F397C0',
'#ff891c',
'#635547',
'#C72228',
'#f79083',
'#EF4E22',
'#989898',
'#7F6874',
'#8870ad',
'#647a4f',
'#EF5A9D',
'#FBBE92',
'#139992',
'#cc7818',
'#DFCDE4',
'#8EC792',
'#C594BF',
'#C3C388',
'#0F4A9C',
'#FACB12',
'#8DB5CE',
'#1A1A1A',
'#C9EBFB',
'#DABE99',
'#65A83E',
'#005579',
'#CDE088',
'#f7f79e',
'#F6BFCB')
tad_boundaries %>% as.data.frame() %>% group_by(seqnames) %>%
summarise(n = n()) %>% ungroup() %>%
ggplot() + geom_col(aes(x = seqnames, y = n, fill = seqnames), alpha = .7, ) +#, position = "dodge")
theme(legend.position = "None") +
scale_fill_manual(values = colPalette_celltypes) +
labs(y = "number of tad boundaries")

TODO: Should I also remove peaks which are across TAD boundaries?
# As input for this function it is best to use only the most highly variable genes
tad_boundaries_p2g_scores <- function(p2g_mat_sub, peak_mat, links, p2g_original, gene_expr, tad_boundaries){
atac_granges <- metadata(p2g_original)[[1]]
#rna_granges <- metadata(p2g_original)[[2]]
gene_anno <- rowData(gene_expr)
# create gene annotations with start coordinate of each gene
# subset to contain only genes which are included in our peak2gene matrix
gene_anno <- gene_anno %>% as.data.frame() %>%
mutate(idxRNA = seq(nrow(.))) %>%
filter(name %in% rownames(p2g_mat_sub)) %>%
mutate(strand = ifelse(strand == 1, "+", "-")) %>%
mutate(start_coord = ifelse(strand == "+", start, end)) %>%
rename(gene = name) #%>% GRanges()
# subset atac granges & get middle of each peak
pos_atac_granges <- atac_granges %>%
as.data.frame() %>%
mutate(idxATAC = seq(nrow(.))) %>%
# group_by(seqnames) %>%
# mutate(idx = seq_along(seqnames)) %>%
# ungroup %>%
#tidyr::unite(chr_idx, seqnames, idx, remove = FALSE, sep = "_") %>%
filter(idxATAC %in% colnames(p2g_mat_sub)) %>%
mutate(middle = start + 300) #%>% GRanges()
#TODO: Filter for genes!
stopifnot(length(unique(links$idxATAC)) == dim(pos_atac_granges)[[1]])
stopifnot(length(unique(links$idxRNA)) == dim(gene_anno)[[1]])
#p2g_filt <- p2g_original %>% as.data.frame() %>% filter(gene %in% rownames(p2g_mat))
# find overlapping peaks and gene window in chromosome-aware fashion
tad_overlaps_genes <- suppressWarnings(findOverlaps(gene_anno %>% GRanges(),
tad_boundaries))
# get genes which are not found within two TAD boundaries, but only within one
within_genes <- (tad_overlaps_genes %>%
as.data.frame() %>%
group_by(queryHits) %>%
summarise(n = n()) %>% ungroup() %>%
filter(n < 2))$queryHits
print(paste0("Out of ", nrow(gene_anno), " genes, ", length(unique(queryHits(tad_overlaps_genes))), " genes are within TAD boundaries. Some of these genes even span across TAD boudnaries, namely ", length(within_genes), "."))
# We only keep genes within boundaries, but not genes crossing boundaries
tad_overlaps_genes <- tad_overlaps_genes %>% as.data.frame %>%
filter(queryHits %in% within_genes) #%>% S4Vectors::as()
# get peaks overlapping with tad boundaries
tad_overlaps_peaks <- suppressWarnings(findOverlaps(pos_atac_granges %>% GRanges(),
tad_boundaries))
# filter for peaks overlapping tad boundaries which also contain genes
tad_overlaps_peaks <- tad_overlaps_peaks %>% as.data.frame() %>%
filter(subjectHits %in% tad_overlaps_genes$subjectHits)
# combine tad boundaries which contain genes and peaks
tad_combine <- left_join(tad_overlaps_genes, tad_overlaps_peaks,
copy = TRUE, by = "subjectHits", suffix = c(".gene", ".peak"))
genes <- gene_anno[tad_combine$queryHits.gene, ] %>%
mutate(tad_index = tad_combine$subjectHits)
peak_coll <- pos_atac_granges[tad_combine$queryHits.peak, ] %>%
mutate(tad_index = tad_combine$subjectHits)
gene_peak_tad_df <- left_join(genes, peak_coll, by = "tad_index", suffic = c(".gene", ".peak")) %>% unite(peak_gene_tad, gene, idxATAC, sep = "#", remove = FALSE)
### some plots
p1 <- (tad_overlaps_peaks %>% as.data.frame() %>%
group_by(subjectHits) %>% # gene region
summarize(n = n()) %>% # get number of peaks overlapping with a gene region
ggplot() + geom_histogram(aes(x = n), bins = 100, fill="#69b3a2") +
labs(title = "Number of peaks per tad boundary",
x = "number of peaks"))
p2 <- (tad_overlaps_genes %>% as.data.frame() %>%
group_by(subjectHits) %>% # gene region
summarize(n = n()) %>% # get number of peaks overlapping with a gene region
ggplot() + geom_histogram(aes(x = n), bins = 100, fill="#69b3a2") +
labs(title = "Number of hvg genes per tad boundary",
x = "number of genes"))
print(cowplot::plot_grid(p1, p2, ncol = 2))
# combine the annotation dataframe with the p2g links dataframe
p2g_join <- left_join(links, as.data.frame(pos_atac_granges),
by = "idxATAC")
p2g_join <- left_join(p2g_join, as.data.frame(gene_anno),
by = "idxRNA", suffix = c(".atac", ".rna"))
# compute distance
p2g_join <- p2g_join %>%
mutate(distance = abs(start_coord - middle))
# filter for the p2g links within tad boundaries
corr_tad_boundary <- p2g_join %>%
unite(peak_gene_tad, gene, idxATAC, sep = "#", remove = FALSE) %>%
filter(peak_gene_tad %in% gene_peak_tad_df$peak_gene_tad)
### PLOTS
p1 <- corr_tad_boundary %>%
ggplot() +
geom_histogram(aes(x = Correlation), bins = 200, fill = "#69b3a2") +
labs(title = "Correlation values of peak-to-gene links found within tad boundaries")
p2 <- corr_tad_boundary %>%
ggplot() +
geom_histogram(aes(x = distance), bins = 200, fill = "#69b3a2") +
labs(title = "Distance between peaks and TSS within tad boundary")
p3 <- corr_tad_boundary %>%
mutate(bin = cut_width(distance, width=100000, boundary=0)) %>%
ggplot() +
geom_boxplot(aes(x = bin, y = Correlation), fill = "#69b3a2") +
labs(title = "Distance and Correlation within tad boundary, 100kb bins",
x = "Distance (100kb bins)") +
scale_x_discrete(guide = guide_axis(angle = 90))
print(cowplot::plot_grid(p1, p2, p3, ncol = 1))
#### PLOT
p2 <- corr_tad_boundary %>% ggplot() +
geom_histogram(aes(x = distance), bins = 500) +
scale_y_log10() +
labs(title = "Relative distance of peaks to TSS within tad boundaries",
x = "Relative distance to TSS", y = "log10(count)") +
geom_vline(xintercept = c(100000, -100000), color = "red")
print(p2)
#cowplot::plot_grid(p1, p2, ncol = 1)
p2g_links_tad <- Matrix::sparseMatrix(
i = corr_tad_boundary$idxRNA,
j = corr_tad_boundary$idxATAC,
x = corr_tad_boundary$Correlation,
dims = c(nrow(expr_mat), nrow(peak_mat)),
dimnames = list(rownames(expr_mat),rownames(peak_mat))
)
print(paste0("The maximum value is: ", max(p2g_links_tad), ", the minum value is: ", min(p2g_links_tad) ))
p2g_links_tad <- p2g_links_tad[rowSums(p2g_links_tad) != 0, ]
p2g_links_tad <- p2g_links_tad[, colSums(p2g_links_tad) != 0]
print(paste0("After removing any rows and columsn which do not contain any links we are left with ", nrow(p2g_links_tad), " genes and ", ncol(p2g_links_tad), " peaks."))
# Compute gene activity scores
tad_scores <- gene_activity_scores(peak_mat_sub[colnames(p2g_links_tad), ], p2g_links_tad)
return(tad_scores)
}
gc(reset = TRUE)
## used (Mb) gc trigger (Mb) max used (Mb)
## Ncells 7542060 402.8 21423616 1144.2 7542060 402.8
## Vcells 4387697200 33475.5 7384002436 56335.5 4387697200 33475.5
tad_scores <- tad_boundaries_p2g_scores(p2g_mat_sub = p2g_mat_sub,
peak_mat = peak_mat,
links = links,
p2g_original = p2g,
gene_expr = gene_expr,
tad_boundaries = tad_boundaries)
## [1] "Out of 1285 genes, 1090 genes are within TAD boundaries. Some of these genes even span across TAD boudnaries, namely 1038."


## Warning: Transformation introduced infinite values in continuous y-axis
## Warning: Removed 244 rows containing missing values (geom_bar).

## [1] "The maximum value is: 0.969061504385395, the minum value is: 0"
## [1] "After removing any rows and columsn which do not contain any links we are left with 1028 genes and 17390 peaks."
## [1] "normalized the p2g matrix"
## [1] "Computed weightes sum of peaks for each gene and cell"
## [1] "Normalized for library size"
## [1] "Exponentiated matrix"
## [1] "Divided by total activity to get value between zero and one"
gc(reset = TRUE)
## used (Mb) gc trigger (Mb) max used (Mb)
## Ncells 7944922 424.4 21423616 1144.2 7944922 424.4
## Vcells 5484933292 41846.8 8860882923 67603.2 5484933292 41846.8
gene_window_agg <- create_seacell_aggregates(tad_scores, seacells)
gene_window_corr <- rowwise_correlations(seacell_rna_agg, gene_window_agg,
name = "Gene window around TSS")
gene_window_corr[[2]]

ggplot() +
geom_point(aes(y = seacell_corr_archr[[1]][names(gene_window_corr[[1]])],
x =gene_window_corr[[1]])) +
geom_density_2d_filled(aes(y = seacell_corr_archr[[1]][names(gene_window_corr[[1]])],
x = gene_window_corr[[1]]), alpha = 0.5) +
geom_line(aes(x = seacell_corr_archr[[1]][names(gene_window_corr[[1]])],
y = seacell_corr_archr[[1]][names(gene_window_corr[[1]])],
color = "red")) +
theme(legend.position = "None") +
labs(x = "Correlation between gene expression and p2g activity scores, tad boundary",
y = "Correlation between gene expression and ArchR gene activity scores",
title = "Peak-to-gene links within tad boundaries")

ggplot() +
geom_point(aes(y = seacell_corr_p2g[[1]][names(gene_window_corr[[1]])],
x =gene_window_corr[[1]])) +
geom_density_2d_filled(aes(y = seacell_corr_p2g[[1]][names(gene_window_corr[[1]])],
x = gene_window_corr[[1]]), alpha = 0.5) +
geom_line(aes(x = seacell_corr_p2g[[1]][names(gene_window_corr[[1]])],
y = seacell_corr_p2g[[1]][names(gene_window_corr[[1]])],
color = "red")) +
theme(legend.position = "None") +
labs(x = "Correlation, tad boundaries",
y = "Correlation, all p2g links ",
title = "Peak-to-gene links within tad boundaries vs. all links")

Adapted Archr Gene Activity Score function
ArchR provides a function to compute gene activity scores based on accessibility in the regions around the gene. For this a tile matrix is used. This tile matrix is a matrix where the genome is divided into bins of 500bp. If there is a Tn5 insertion in a bin the entry will be 1, if there is no insertion the entry will be 0. Importantly, they compared their function to 52 other functions and found their own function to be the best performing.
Here I tried to better understand how this function works and changed the source code of the ArchR function to also take peak matrix as input and compute the gene activity based on peaks, rather than based on tiles. Additionally, I adapted the funciton in a way such that it takes tad boundaries as input and uses all peaks which are within the same tad boundary as a gene to compute the activity scores.
There are two different options for computing gene activity scores in ArchR. First, we can use the TSS and create a gene window around it (+/- 100kp of TSS). All insertions found within tiles within this gene window will be accumulated for the gene activity scores. If we set the option ‘useGeneBoundaries=TRUE’ then we will make sure that no regions overlap between any two genes. If the gene window of one gene overlaps with the gene window of another gene, those tiles are not considered anymore. The disadvantage of this approac is that genes can be very large (>100bp), meaning that in some cases the 100kp extension downstream of the TSS would not even contain the entire gene body.
Second, we can use the entire gene body and extend the gene window beyond the start and end coordinates of the gene body. Importantly, the gene body is extended 5kb upstream of the TSS, to also include the promoter region. Using the entire gene body instead of only the TSS can be achieved by setting ‘useTSS=FALSE’. In this approach the gene window is created by extending -100kb upstream of the TSS -5kb and +100kb downstream of the gene end coordinate. This way, the entire gene body will be included in the gene window. An unwanted consequence of this might be that very large genes could bias the gene activity scores. Therefore ArchR introduces a weight for the inverse of the gene body size according to:
\(w = \frac{1}{gene size}\) with \(w\) being the inverse of the gene size. $
geneRegions\(geneWeight <- 1 + m * (geneScaleFactor - 1) / (max(m) - min(m))\)
Additionally, ArchR uses a distance weight. Farther away tiles/peaks are less likely to interact with a TSS than closer tiles/peaks. If the first approach, using only the TSS, the distance weights are computed as follows:
\(weight = e^{-(abs(distTSS/5000))}\) with \(distTSS\) being the distance from the TSS. This way the weights decay exponentially with distance. The constant value of \(5000\) is a parameter which could be optimized for different genes or datasets, but here we will keep it constant.
In case the entire gene body is used, the distance weights are kept constant for all tiles/peaks within the gene body and only decay beyond the gene body.
\(weight = \begin{cases} if (-5kb from TSS, TTS): 1 + e^{-1} \\ else: e^{-abs(distGB/5000) + e^{-1}} \end{cases}\)
ArchR Gene Activity Scores using TAD boundaries
Instead of using a +/-100kb window around the gene body, in the adapted function all peaks which are within the same TAD boundary as the gene of interest are considered for the activity score of that gene. The distance weight with c = 5000 is kept the same as for the default ArchR function. As can be seen below, extending the gene window to TAD boundaries yields very similar results compared to the default ArchR function.
proj <- loadArchRProject("12_Copy1/")
# proj <- addTADGeneScoreMatrix(
# proj,
# genes = getGenes(proj),
# peaks = getPeakSet(proj),
# tadBoundaries = tad_boundaries,
# geneModel = "exp(-abs(x)/5000) + exp(-1)",
# matrixName = "GeneScoreMatrix",
# extendUpstream = c(1000, 100000),
# extendDownstream = c(1000, 100000),
# geneUpstream = 5000, #New Param
# #geneDownstream = 0, #New Param
# useGeneBoundaries = FALSE,
# useTSS = FALSE, #New Param
# extendTSS = FALSE,
# tileSize = 500,
# ceiling = 4,
# geneScaleFactor = 5, #New Param
# scaleTo = 10000,
# excludeChr = c("chrY", "chrX", "chrM"),
# blacklist = getBlacklist(proj),
# threads = 1,
# parallelParam = NULL,
# subThreading = TRUE,
# force = TRUE,
# logFile = createLogFile(".addTADGeneScoreMat"))
scores <- getMatrixFromProject(proj, useMatrix = "GeneScoreMatrix")
score_mat <- assays(scores)[[1]]
rownames(score_mat) <- rowData(scores)$name
#saveRDS(scores, "tad_scores")
ggplot() +
geom_histogram(aes(x = rowSums(score_mat)), bins = 200)
# compute aggregates of ArchR gene activity score matrix
default_archr <- create_seacell_aggregates(archr_scores_mat,
seacells)
# compute aggregates for tad boundary ArchR gene activity score matrix
tad_archr <- create_seacell_aggregates(score_mat, seacells)
# compute aggregates of gene expression matrix
rna_hvg <- create_seacell_aggregates(expr_sub, seacells)
# correlation between gene expression values and default Archr gene activity scores
default_archr_corr <- rowwise_correlations(rna_hvg, default_archr,
"ArchR gene activity scores, SEAcells")
# correlation between gene expression and TAD boundary gene activity scores
tad_corr <- rowwise_correlations(rna_hvg, tad_archr, "ArchR gene activity scores within TAD boundaries, SEACells")
cowplot::plot_grid(default_archr_corr[[2]], tad_corr[[2]], ncol = 2)
ggplot() +
geom_point(aes(x = tad_corr[[1]], y = default_archr_corr[[1]][names(tad_corr[[1]])])) +
geom_density_2d_filled(aes(x = tad_corr[[1]],
y = default_archr_corr[[1]][names(tad_corr[[1]])]),
alpha = .5) +
geom_line(aes(x = default_archr_corr[[1]], y = default_archr_corr[[1]]), col = "red") +
theme(legend.position = "None") +
labs(x = "Correlation gene expression & ArchR TAD boundary scores",
title = "Restricting ArchR scores to within TAD boundaries",
y = "Correlation gene expression & ArchR gene activity scores")
TAD boundares E7.5
Since the TAD boundaries used here, are from gastrulation day E7.5. For the later time points no TAD boundaries are available. Therefore, in the following I will check if the results improve in comparison to the default ArchR function when using only data from E7.5. Since during gastrulation TAD boundaries might still be very dynamic the improving effect of TAD boundaries could be diluted by later time points in the data.
What are th genes which get zero activity scores? Do they lie outside the TAD boundaries?
e75_meta <- colData(scores) %>% as.data.frame() %>%
filter(Sample %in% c("E7.5_rep1", "E7.5_rep2")) %>%
rownames_to_column("cell")
mat_75 <- score_mat[rownames(score_mat) %in% rownames(expr_sub), e75_meta$cell]
seacells_sub <- seacells %>% filter(index %in% colnames(mat_75))
# compute aggregates of ArchR gene activity score matrix
default_archr <- create_seacell_aggregates(archr_scores_mat[rownames(archr_scores_mat) %in%
rownames(expr_sub),
e75_meta$cell],
seacells_sub)
# compute aggregates for tad boundary ArchR gene activity score matrix
tad_archr <- create_seacell_aggregates(mat_75, seacells_sub)
# compute aggregates of gene expression matrix
rna_hvg <- create_seacell_aggregates(expr_sub[, e75_meta$cell], seacells_sub)
# correlation between gene expression values and default Archr gene activity scores
default_archr_corr <- rowwise_correlations(rna_hvg, default_archr,
"ArchR gene activity scores, SEAcells")
# correlation between gene expression and TAD boundary gene activity scores
tad_corr <- rowwise_correlations(rna_hvg, tad_archr, "ArchR gene activity scores within TAD boundaries, SEACells")
cowplot::plot_grid(default_archr_corr[[2]], tad_corr[[2]], ncol = 2)
ggplot() +
geom_point(aes(x = tad_corr[[1]], y = default_archr_corr[[1]][names(tad_corr[[1]])])) +
geom_density_2d_filled(aes(x = tad_corr[[1]],
y = default_archr_corr[[1]][names(tad_corr[[1]])]),
alpha = .5) +
geom_line(aes(x = default_archr_corr[[1]], y = default_archr_corr[[1]]), col = "red") +
theme(legend.position = "None") +
labs(x = "Correlation gene expression & ArchR TAD boundary scores",
title = "Restricting ArchR scores to within TAD boundaries",
y = "Correlation gene expression & ArchR gene activity scores")
What are the genes which get zero correlation with gene expression?
There are 8 genes which get zero correlation values between gene activity scores and gene expression. This is, because they get zero activity scores in all cells. However, the same genes are expressed to certain levels according to the gene expression matrix. Two of the genes also get zero activity scores in the default ArchR function (Prl2c3, Gsdmc4). The reason for is not immediately clear, since as long as there are peaks in a gene window, the distance weight will at least be 0.36 accorindg to the formula. One reason for zero values could be that these genes lie outside TAD boundaries wich is in fact the case for four out of 8 genes.
What is the explanation why Lyz2 and Gm13547 get activity scores of zero?
zero_genes <- names(tad_corr[[1]][tad_corr[[1]] == 0])
zero_mat <- score_mat[zero_genes, ]
rowSums(zero_mat)
# check the default ArchR scores for these genes
rowSums(archr_scores_mat[zero_genes, ])
# check the gene expression coutns for these genes
rowSums(expr_mat[zero_genes,])
p2g_pos <- p2g %>% as.data.frame() %>% filter(Correlation > 0) %>%
unite(link, idxRNA, idxATAC, sep = "%", remove = FALSE)
gene_anno_all <- rowData(gene_expr) %>% as.data.frame() %>%
mutate(idxRNA = seq(nrow(.))) %>%
filter(idxRNA %in% p2g_pos$idxRNA) %>%
mutate(strand = ifelse(strand == 1, "+", "-")) %>%
mutate(start_coord = ifelse(strand == "+", start, end)) %>%
rename(gene = name) #%>% GRanges()
# subset atac granges & get middle of each peak
pos_atac_granges_all <- metadata(p2g)[[1]] %>%
as.data.frame() %>%
mutate(idxATAC = seq(nrow(.))) %>%
# group_by(seqnames) %>%
# mutate(idx = seq_along(seqnames)) %>%
# ungroup %>%
#tidyr::unite(chr_idx, seqnames, idx, remove = FALSE, sep = "_") %>%
filter(idxATAC %in% p2g_pos$idxATAC) %>%
mutate(middle = start + 300) #%>% GRanges()
# combine the three dataframes
p2g_join_all <- left_join(p2g_pos, as.data.frame(pos_atac_granges_all),
by = "idxATAC")
p2g_join_all <- left_join(p2g_join_all, as.data.frame(gene_anno_all),
by = "idxRNA", suffix = c(".atac", ".rna"))
p2g_join_all <- p2g_join_all %>%
mutate(distance = abs(start_coord - middle))
# find overlapping peaks and gene window in chromosome-aware fashion
tad_overlaps_genes <- (findOverlaps(gene_anno_all %>% GRanges(),
tad_boundaries))
# get all genes which are found within tad boudnaries
gene_anno_within_tad <- gene_anno_all[queryHits(tad_overlaps_genes),]
# Lets examine the genes which are found within tad boundaries, but
# get an activity score of zero nevertheless
gene_anno_within_tad %>% filter(gene %in% zero_genes)
gene_name = "Lyz2"
chr_name = "chr2"
chrx <- tad_boundaries %>% as.data.frame() %>% filter(seqnames == chr_name) %>%GRanges()
hits <- findOverlaps(gene_anno_all %>% filter(gene == gene_name) %>% GRanges(), chrx)
start_tad <- start(chrx[subjectHits(hits),])
end_tad <- end(chrx[subjectHits(hits),])
start_gene <- start(gene_anno_all %>% filter(gene == gene_name) %>% GRanges())
end_gene <- end(gene_anno_all %>% filter(gene == gene_name) %>% GRanges())
print(paste0("Out of ", length(zero_genes), " genes, ", length(zero_genes[zero_genes %in% gene_anno_within_tad$gene]) , " genes are found within tad boundaries, while the rest are not."))
pos_atac_granges_all %>% as.data.frame() %>% filter(seqnames == chr_name) %>%
filter(start > start_tad & end < end_tad)
#
# zero_genes
#
# idx <- (gene_anno_all %>% filter(gene %in% zero_genes))$idxRNA
#
# idx %in% gene_anno_all[tad_overlaps_genes$queryHits,
ArchR Gene Activity Scores using gene body
ArchR Gene Activity Scores using gene body
#saveArchRProject(ArchRProj = proj, outputDirectory = "12_Copy4/", load = FALSE)
loadArchRProject("12_activity_scores_gene_body_peaks/")
proj <- addKathiGeneScoreMatrix(
proj,
genes = getGenes(proj),
peaks = getPeakSet(proj),
geneModel = "exp(-abs(x)/5000) + exp(-1)",
matrixName = "GeneScoreMatrix",
extendUpstream = c(1000, 100000),
extendDownstream = c(1000, 100000),
#geneUpstream = 5000, #New Param
#geneDownstream = 0, #New Param
useGeneBoundaries = TRUE,
useTSS = FALSE, #New Param
extendTSS = FALSE,
tileSize = 500,
ceiling = 4,
geneScaleFactor = 5, #New Param
scaleTo = 10000,
excludeChr = c("chrY", "chrM"),
blacklist = getBlacklist(proj),
threads = 1,
parallelParam = NULL,
subThreading = TRUE,
force = TRUE,
logFile = createLogFile(".addKathiGeneScoreMat"))
scores <- getMatrixFromProject(proj, useMatrix = "GeneScoreMatrix")
scores_mat <- assays(scores)[[1]]
rownames(scores_mat) <- rowData(scores)$name
# sce <- SingleCellExperiment(list(scores=scores_mat),
# rowData = as.data.frame(rowData(scores)),
# colData = as.data.frame(colnames(scores_mat)))
#
# writeH5AD(sce, "/omics/groups/OE0533/internal/katharina/scDoRI/gastrulation_data/jupyter_notebooks/p2g_gene_activity_scores/archr_scores_gene_body_peak_based", X_name = "scores")
Correlating gene expression with activity scores:
archr_gene_body_agg <- knn_aggregates(scores_mat, cell_agg_list)
gene_body_knn <- rowwise_correlations(rna_agg, archr_gene_body_agg, "ArchR gene activity scores based on peak matrix, using gene body")
cowplot::plot_grid(archr_knn[[2]], gene_body_knn[[2]], ncol = 2)
p1 <- ggplot() + geom_density_2d_filled(aes(x = gene_body_knn[[1]],
y = archr_knn[[1]]), alpha = .5) +
geom_point(aes(x = gene_body_knn[[1]], y = archr_knn[[1]])) +
geom_line(aes(x = gene_body_knn[[1]], y = gene_body_knn[[1]]), col = "red") +
theme(legend.position = "None")
ArchR Gene Activity Scores using TSS, no gene body
ArchR Gene Activity Scores using TSS, no gene body
proj <- loadArchRProject("12_activity_scores_TSS_tiles/")
proj <- addGeneScoreMatrix(
proj,
genes = getGenes(proj),
geneModel = "exp(-abs(x)/5000)",
matrixName = "GeneScoreMatrix",
extendUpstream = c(1000, 100000),
extendDownstream = c(1000, 100000),
#geneUpstream = 5000, #New Param
#geneDownstream = 0, #New Param
useGeneBoundaries = TRUE,
useTSS = TRUE, #New Param
extendTSS = FALSE,
tileSize = 500,
ceiling = 4,
geneScaleFactor = 5, #New Param
scaleTo = 10000,
excludeChr = c("chrY", "chrM"),
blacklist = getBlacklist(proj),
threads = 1,
parallelParam = NULL,
subThreading = TRUE,
force = TRUE,
logFile = createLogFile(".addGeneScoreMatrix"))
scores <- getMatrixFromProject(proj, useMatrix = "GeneScoreMatrix")
scores_mat <- assays(scores)[[1]]
rownames(scores_mat) <- rowData(scores)$name
# sce <- SingleCellExperiment(list(scores=scores_mat),
# rowData = as.data.frame(rowData(scores)),
# colData = as.data.frame(colnames(scores_mat)))
#
# writeH5AD(sce, "/omics/groups/OE0533/internal/katharina/scDoRI/gastrulation_data/jupyter_notebooks/p2g_gene_activity_scores/archr_scores_tss", X_name = "scores")
ArchR gene activity scores computed using TSS, no gene body and PeakMatrix instead of TileMatrix
ArchR gene activity scores computed using TSS, no gene body and PeakMatrix instead of TileMatrix
proj <- loadArchRProject("12_activity_scores_TSS_peaks/")
proj <- addKathiGeneScoreMatrix(
proj,
genes = getGenes(proj),
peaks = getPeakSet(proj),
geneModel = "exp(-abs(x)/5000)",
matrixName = "GeneScoreMatrix",
extendUpstream = c(1000, 100000),
extendDownstream = c(1000, 100000),
#geneUpstream = 5000, #New Param
#geneDownstream = 0, #New Param
useGeneBoundaries = TRUE,
useTSS = TRUE, #New Param
extendTSS = FALSE,
tileSize = 500,
ceiling = 4,
geneScaleFactor = 5, #New Param
scaleTo = 10000,
excludeChr = c("chrY", "chrM"),
blacklist = getBlacklist(proj),
threads = 1,
parallelParam = NULL,
subThreading = TRUE,
force = TRUE,
logFile = createLogFile(".addKathiGeneScoreMat"))
scores <- getMatrixFromProject(proj, useMatrix = "GeneScoreMatrix")
scores_mat <- assays(scores)[[1]]
rownames(scores_mat) <- rowData(scores)$name
#
# sce <- SingleCellExperiment(list(scores=scores_mat),
# rowData = as.data.frame(rownames(scores_mat)),
# colData = as.data.frame(colnames(scores_mat)))
#
# writeH5AD(sce, "/omics/groups/OE0533/internal/katharina/scDoRI/gastrulation_data/jupyter_notebooks/p2g_gene_activity_scores/archr_scores_peak_based", X_name = "scores")
# sce <- SingleCellExperiment(list(p2g_mat = p2g_mat))
#
# writeH5AD(sce, "/omics/groups/OE0533/internal/katharina/scDoRI/gastrulation_data/jupyter_notebooks/p2g_gene_activity_scores/p2g_mat_250kb",
# X_name = "p2g_mat")
#
#
# sce <- SingleCellExperiment(list(peak_mat = peak_mat))
#
# writeH5AD(sce, "/omics/groups/OE0533/internal/katharina/scDoRI/gastrulation_data/jupyter_notebooks/p2g_gene_activity_scores/peak_mat",
# X_name = "peak_mat")
# cp_names <- colnames(colData(gene_expr))
# cp_names[20] <- "celltypes"
# colnames(colData(gene_expr)) <- cp_names
sce <- SingleCellExperiment(list(genes = expr_mat),
#rowData = as.data.frame(rownames(gene_expr)),
colData = as.data.frame(colData(gene_expr)))
# writeH5AD(sce, "/omics/groups/OE0533/internal/katharina/scDoRI/gastrulation_data/jupyter_notebooks/p2g_gene_activity_scores/gene_expr_mat",
# X_name = "genes")
#
#
# #p2g_mat_norm <- p2g_mat / rowSums(p2g_mat)
# scores <- p2g_mat %*% peak_mat
# scores <- t(t(scores) / colSums(scores))
# stopifnot(any(is.na(scores)) == FALSE)
# scores@x <- pmin(1e9, exp(scores@x) - 1)
#
#
#
# sce <- SingleCellExperiment(list(investigation = investigation))
#
# writeH5AD(sce, "/omics/groups/OE0533/internal/katharina/scDoRI/gastrulation_data/jupyter_notebooks/p2g_gene_activity_scores/investigation_scores",
# X_name = "investigation")
# latent embedding
emb <- getReducedDims(
ArchRProj = proj,
reducedDims = "atac_LSI_100000",
returnMatrix = TRUE,
dimsToUse = 1:30,
scaleDims = NULL,
corCutOff = 0.75
)
dim(emb)
sce <- SingleCellExperiment(list(embedding = emb))
writeH5AD(sce, "/omics/groups/OE0533/internal/katharina/scDoRI/gastrulation_data/jupyter_notebooks/p2g_gene_activity_scores/archr_lsi_embedding",
X_name = "embedding")
Granja JM, Pierce SE, Corces MR. 2021. ArchR Is a Scalable Software Package for Integrative Single-Cell Chromatin Accessibility Analysis. Nat Genet. Vols. 53(3):403-411. doi: 10.1038/s41588-021-00790-6.
Persad, Sitara, Zi-Ning Choo, Christine Dien, Ignas Masilionis, Ronan Chaligné, Tal Nawy, Chrysothemis C Brown, Itsik Pe’er, Manu Setty, and Dana Pe’er. 2022. “SEACells: Inference of Transcriptional and Epigenomic Cellular States from Single-Cell Genomics Data.” bioRxiv. Cold Spring Harbor Laboratory. https://doi.org/10.1101/2022.04.02.486748.
LS0tCnRpdGxlOiAiSW52ZXN0aWdhdGluZyBwMmdfbWF0IgpiaWJsaW9ncmFwaHk6IHJlZmVyZW5jZXMuYmliCm91dHB1dDogCiAgaHRtbF9kb2N1bWVudDoKICAgIHRvYzogdHJ1ZQogICAgdG9jX2RlcHRoOiAyCiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUKICAgIHRvY19mbG9hdDogdHJ1ZQogICAgY29kZV9kb3dubG9hZDogdHJ1ZQogICAgdGhlbWU6IGNvc21vCiAgICBoaWdobGlnaHQ6IHRleHRtYXRlCi0tLQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGNhY2hlID0gRkFMU0UpCmtuaXRyOjpvcHRzX2tuaXQkc2V0KHJvb3QuZGlyID0gIi9vbWljcy9ncm91cHMvT0UwNTMzL2ludGVybmFsL2thdGhhcmluYS9zY0RvUkkvZ2FzdHJ1bGF0aW9uX2RhdGEiKQpzZXR3ZCgiL29taWNzL2dyb3Vwcy9PRTA1MzMvaW50ZXJuYWwva2F0aGFyaW5hL3NjRG9SSS9nYXN0cnVsYXRpb25fZGF0YSIpCnNldC5zZWVkKDEpCmBgYAoKYGBge3J9CnN1cHByZXNzUGFja2FnZVN0YXJ0dXBNZXNzYWdlcyh7CgpsaWJyYXJ5KEFyY2hSKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShTaW5nbGVDZWxsRXhwZXJpbWVudCkKbGlicmFyeSh6ZWxsa29udmVydGVyKQpsaWJyYXJ5KGR0d2NsdXN0KQp9KQpgYGAKCmBgYHtyfQpwcm9qIDwtIGxvYWRBcmNoUlByb2plY3QoIjExX2FkZGVkX1JpY2FyZHNfcGVha3NfcDJnX2VudGlyZV9jaHJvbW9zb21lIiwgc2hvd0xvZ28gPSBGQUxTRSkKI3NhdmVBcmNoUlByb2plY3QocHJvaiwgIi9vbWljcy9ncm91cHMvT0UwNTMzL2ludGVybmFsL2thdGhhcmluYS9zY0RvUkkvZ2FzdHJ1bGF0aW9uX2RhdGEvMTFfYWRkZWRfUmljYXJkc19wZWFrc19wMmdfZW50aXJlX2Nocm9tb3NvbWUiKQoKYGBgCgojIFAyRy1saW5rIG1hdHJpeCAKCkluIG9yZGVyIHRvIGdldCBnZW5lIHJlZ3VsYXRvcnkgbGlua3MsIG9uZSBjYW4gY29tcHV0ZSBhIHNpbXBsZSBjb3JyZWxhdGlvbgpiZXR3ZWVuIGFjY2Vzc2libGUgcGVha3MgYW5kIGdlbmUgZXhwcmVzc2lvbiwgaW4gb3JkZXIgdG8gZmluZCBwZWFrcyB3aG9zZSBhY3Rpdml0eQppcyBoaWdobHkgY29ycmVsYXRlZCB3aXRoIGV4cHJlc3Npb24gb2YgYSBjZXJ0YWluIGdlbmUuIFRoZXNlIGFyZSBwb3RlbnRpYWwgCmVuaGFuY2VycyBvZiBhIGdlbmUuIFNpbmNlIHNpbmdsZSBjZWxsIGRhdGEgaXMgdmVyeSBzcGFyc2UsIHRoZSBjb21tb24gd2F5IHRvIApjb21wdXRlIGNvcnJlbGF0aW9zbiBpcyBieSBhZ2dyZWdhdGluZyBhY2Nlc3NpYmlsaXR5IGFuZCBnZW5lIGV4cHJlc3Npb24gZGF0YQphY3Jvc3MgY2VsbCBhZ2dyZWdhdGVzLiBJbiBBcmNoUiB0aGlzIGlzIGRvbmUgYnkgc2FtcGxpbmcgNTAwIGNlbGxzIGZyb20gdGhlIAplbnRpcmUgZGF0YXNldCBhbmQgZmluZGluZyB0aGUgNTAgbmVhcmVzdCBuZWlnaGJvcnMgb2YgdGhlc2UgY2VsbHMuIFRoZXNlIGNlbGwKYWdncmVnYXRlcyB0aGVyZWZvcmUgcmVwcnNlbnQgZ3JvdXBzIG9mIHNpbWlsYXIgY2VsbHMgYW5kIGNhbiBiZSB1c2VkIHRvIGNvbXB1dGUKY29ycmVsYXRpb25zLiAKCk5vdGFibHksIGZvciBjb21wdXRpbmcgbGlua3Mgd2l0aGluIGEgY2VydGFpbiBkaXN0YW5jZSBvbiB0aGUgY2hyb21vc29tZSwgQXJjaFIKZG9lcyBub3QgdGFrZSBpbnRvIGNvbnNpZGVyYXRpb24gdGhlIHN0cmFuZCBvcmllbnRhdGlvbiwgYnV0IGNvbXB1dGVzIHRoZSAKZGlzdGFuY2UgYmV0d2VlbiB0aGUgInN0YXJ0IiBjb29yZGluYXRlIGFuZCB0aGUgcGVhayBtaWRkbGUgY29vcmRpbmF0ZS4gSG93ZXZlciwKb24gdGhlIG1pbnVzIHN0cmFuZCB0aGUgVFNTIGlzIHRoZSAiZW5kIiBjb29yZGluYXRlLiBGb3IgdGhlIGNvcnJlbGF0aW9ucyB0aGlzCmlzIG5vdCBpbXBvcnRhbnQsIGJ1dCBpbiBteSBjb21wdXRhdGlvbnMgSSB3aWxsIHVzZSB0aGUgVFNTIGNvb3JkaW5hdGUgaW4KYSBzdHJhbmQtYXdhcmUgZmFzaGlvbi4KCgpCYXNlZCBvbiB0aGVzZSBwdXRhdGl2ZSBwZWFrLXRvLWdlbmUgbGlua3MsIGl0IGlzIHBvc3NpYmxlIHRvIGNvbXB1dGUgZ2VuZSBhY3Rpdml0eQpzY29yZXMuIElmIHRoZXNlIHNjb3JlcyByZWNhcGl0dWxhdGUgZ2VuZSBleHByZXNzaW9uIHdlbGwsIHRoaXMgaXMgYSAKdmFsaWRhdGlvbiBvZiB0aGUgbGlua3MuIE5ldmVydGhlbGVzcywgaXQgaXMgZXhwZWN0ZWQgdGhhdCBnZW5lIGFjdGl2aXR5IHNjb3Jlcwpjb3JyZWxhdGUgaGlnaGx5IHdpdGggZ2VuZSBleHByZXNzaW9uLCBzaW5jZSB0aGUgcGVha3MgdXNlZCBmb3IgdGhlIGNvbXB1dGF0aW9uIAphcmUgaGlnaGx5IGNvcnJlbGF0ZWQgd2l0aCB0aGUgZ2VuZXMgcGVyIGRlZmluaXRpb24uIEluIHRoZSBmb2xsb3dpbmcgeW91IHdpbGwgZmluZCBhIGZ1bmN0aW9uIHRvIGNvbXB1dGUgZ2VuZSBhY3Rpdml0eSBzY29yZXMgZnJvbSBwZWFrLXRvLWdlbmUgbGlua3MsIGFkcGF0ZWQgZnJvbSAKQ2ljZXJvLCB3aGVyZSBnZW5lIGFjdGl2aXQgc2NvcmVzIGFyZSBjb21wdXRlZCBmcm9tIGNvLWFjY2Vzc2libGUgcGVha3MuCgpGdXJ0aGVybW9yZSwgc2luY2UgcGVha3Mgd2hpY2ggYXJlIGZhcnRoZXIgYXdheSBmcm9tIGEgZ2VuZSBvbiB0aGUgZ2Vub21lIGFyZSBsZXNzIGxpa2VseSAKdG8gcmVndWxhdGUgdGhpcyBnZW5lLCBpdCBpcyBjb21tb24gdG8gdXNlIGRpc3RhbmNlIHdlaWd0aHMgdG8gcGVuYWxpemUgcGVha3Mgd2hpY2gKYXJlIGhpZ2hseSBjb3JyZWxhdGVkLCBidXQgZGlzdGFudC4gSG93ZXZlciwgSSBvYnNlcnZlZCB0aGF0IHVzaW5nIGRpc3RhbmNlIHdlaWd0aHMKZGVjcmVhc2VzIHRoZSBjb3JyZWxhdGlvbiB3aXRoIGdlbmUgZXhwcmVzc2lvbiwgd2l0aCBsZXNzIHN0ZWVwIGRlY2F5IHJhdGVzIApyZXN1bHRpbmcgaW4gYmV0dGVyIGdlbmUgYWN0aXZpdHkgc2NvcmVzLiBUaGlzIHNob3dzIHRoYXQgdGhlIGRpc3RhbmNlIHdlaWdodHMgCmxlYWQgdG8gYSBsb3Qgb2YgemVybyB2YWx1ZXMgd2hlbiBtdWx0aXBsaWVkIHdpdGggdGhlIGNvcnJlbGF0aW9uIHZhbHVlcy4gU3RpbGwsCmJpb2xvZ2ljYWxseSB2ZXJ5IGRpc3RhbnQgcGVha3MgYXJlIHByb2JhYmx5IG5vdCBjb3JyZWxhdGVkIHdpdGggYSBwcm9tb3RlciwgYmVjYXVzZSAKdGhleSBhcmUgaW50ZXJlYWN0aW5nLCBidXQgbW9yZSBsaWtlbHksIGFyZSBhcmJpdHJhcnkgY29ycmVsYXRpb25zLiBGaW5kaW5nIGEgCmdvb2QgdHJhZGUtb2ZmIGJldHdlZW4gY29uc2lkZXJpbmcgY29ycmVsYXRpb25zIGFjcm9zcyBsYXJnZSBkaXN0YW5jZXMsIGJ1dCBhbHNvCnVzaW5nIHByaW9yIGtub3dsZWRnZSBhYm91dCB0aGUgYmlvbG9neSBhbmQgcmVzdHJpY3RpbmcgdGhlIGxpbmtzIHRvIGEgY2VydGFpbiAKd2luZG93IGlzIG5vbi10cml2aWFsLiBUaGUgY29ycmVjdCBkaXN0YW5jZSBkZWNheSByYXRlICBwcm9iYWJseSBkZXBlbmRzIG9uIGVhY2ggaW5kaXZpZHVhbCBnZW5lIGFuZCBtaWdodCBkaWZmZXIgYWNyb3NzIGNlbGx0eXBlcy4KCllldCBhbm90aGVyIGFwcHJvYWNoIHdvdWxkIGJlIHRvIHVzZSBvbmx5IHBlYWtzLCB3aGljaCBhcmUgd2l0aGluICsvLSAxMDBrYiBvZiB0aGUKVFNTIG9mIGEgZ2VuZSwgdGhlcmVieSwgcmVtb3ZpbmcgYW55IHBlYWtzIHdoaWNoIGFyZSBmYXIgYXdheS4gVGhpcyBpcyBzaW1pbGFyIHRvIAp0aGUgYXBwcm9hY2ggaW4gQXJjaFIuIEhlcmUsIHRoZSBnZW5lIGFjdGl2aXR5IHNjb3JlcyBmb3IgZWFjaCBnZW5lIGFyZSBjb21wdXRlZApiYXNlZCBvbiBhbGwgcGVha3Mgd2hpY2ggYXJlIHdpdGhpbiArLy0gMTAwYnAgb2YgdGhlIFRTUyBvZiB0aGUgZ2VuZS4gQXMgeW91IAp3aWxsIHNlZSBpbiB0aGUgZm9sbG93aW5nIHBsb3RzIHVzaW5nIHRoaXMgYXBwcm9hY2ggbGVhZHMgdG8gdmVyeSBoaWdoIGNvcnJlbGF0aW9ucwpiZXR3ZWVuIGdlbmUgZXhwcmVzc2lvbiBhbmQgZ2VuZSBhY3Rpdml0eSBzY29yZXMuIENvbXB1dGluZyB0aGUgc2NvcmVzIGJhc2VkIG9uIApwZWFrLXRvLWdlbmUgbGlua3Mgb2ZmZXJzIG9ubHkgYSBtaW5vciBpbXByb3ZlbWVudC4gCgpUaGUgbWFpbiBwdXJwb3NlIG9mIHRoZSBlbnRpcmUgZXhjZXJjaXNlIHdhcyB0byBiZSBhYmxlIHRvIGNvbXBhcmUgdGhlIGdvb2RuZXNzIApvZiB0aGVzZSBsaW5rcyB0byBsaW5rcyBvYnRhaW5lZCB1c2luZyBzY0RvUmkuIFRoaXMgcmVsYXRpb25zaGlwIHdpbGwgaGF2ZSB0byBiZSAKZXhwbG9yZWQgZnVydGhlci4gCgpgYGAje3J9CnByb2ogPC0gYWRkUGVhazJHZW5lTGlua3MoQXJjaFJQcm9qID0gcHJvaiwKICByZWR1Y2VkRGltcyAgPSAiYXRhY19MU0lfMTAwMDAwIiwKICB1c2VNYXRyaXggPSAiR2VuZUV4cHJlc3Npb25NYXRyaXgiLAogIG1heERpc3QgPSA0MDAwMDAwMDAsCiAgdmVyYm9zZSA9IFRSVUUKICApCgpwMmcgPC0gZ2V0UGVhazJHZW5lTGlua3MoCiAgQXJjaFJQcm9qID0gcHJvaiwKICBjb3JDdXRPZmYgPSAtMSwKICByZXNvbHV0aW9uID0gMSwKICBGRFJDdXRPZmYgPSAxZS0wNCwKICB2YXJDdXRPZmZBVEFDID0gLjI1LAogIHZhckN1dE9mZlJOQSA9IC4yNSwgCiAgcmV0dXJuTG9vcHMgPSBGQUxTRQopCmBgYAoKCmBgYHtyfQojc2F2ZVJEUyhwMmcsICIvb21pY3MvZ3JvdXBzL09FMDUzMy9pbnRlcm5hbC9rYXRoYXJpbmEvc2NEb1JJL2dhc3RydWxhdGlvbl9kYXRhL1JtZHMvcGVhazJnZW5lX2xpbmtzX2VudGlyZV9jaHJvbW9zb21lXzI1XzA0XzIwMjIiKQpwMmcgPC0gcmVhZFJEUyggIi9vbWljcy9ncm91cHMvT0UwNTMzL2ludGVybmFsL2thdGhhcmluYS9zY0RvUkkvZ2FzdHJ1bGF0aW9uX2RhdGEvUm1kcy9wZWFrMmdlbmVfbGlua3NfZW50aXJlX2Nocm9tb3NvbWVfMjVfMDRfMjAyMiIpCmBgYAoKUmVhZCBpbiB0aGUgcGVhayBhY2Nlc3NpYmlsaXR5IG1hdHJpeCBhbmQgdGhlIGdlbmUgZXhwcmVzc2lvbiBtYXRyaXg6CgpgYGB7cn0KIyBnZXQgcGVhayBtYXRyaXgKcGVha3MgPC0gZ2V0TWF0cml4RnJvbVByb2plY3QocHJvaiwgdXNlTWF0cml4ID0gIlBlYWtNYXRyaXgiLCBiaW5hcml6ZSA9IEZBTFNFKQpwZWFrX21hdCA8LSBhc3NheXMocGVha3MpW1sxXV0KCiMgcmVhZCBpbiBnbmUgZXhwcmVzc3Npb24gbWF0cml4CmdlbmVfZXhwciA8LSBnZXRNYXRyaXhGcm9tUHJvamVjdChwcm9qLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHVzZU1hdHJpeCA9ICJHZW5lRXhwcmVzc2lvbk1hdHJpeCIpCmV4cHJfbWF0IDwtIGFzc2F5cyhnZW5lX2V4cHIpW1sxXV0Kcm93bmFtZXMoZXhwcl9tYXQpIDwtIHJvd0RhdGEoZ2VuZV9leHByKSRuYW1lCgojIHJlYWQgaW4gYXJjaHIgZ2VuZSBhY3Rpdml0eSBzY29yZXMKYXJjaHJfc2NvcmVzIDwtIGdldE1hdHJpeEZyb21Qcm9qZWN0KHByb2osIHVzZU1hdHJpeCA9ICJHZW5lU2NvcmVNYXRyaXgiKQoKY3BfbmFtZXMgPC0gY29sbmFtZXMoY29sRGF0YShhcmNocl9zY29yZXMpKQpjcF9uYW1lc1syMF0gPC0gImNlbGx0eXBlcyIKY29sbmFtZXMoY29sRGF0YShhcmNocl9zY29yZXMpKSA8LSBjcF9uYW1lcwoKYXJjaHJfc2NvcmVzX21hdCA8LSBhc3NheXMoYXJjaHJfc2NvcmVzKVtbMV1dCnJvd25hbWVzKGFyY2hyX3Njb3Jlc19tYXQpIDwtIHJvd0RhdGEoYXJjaHJfc2NvcmVzKSRuYW1lCmBgYAoKV2Ugd2lsbCBvbmx5IHVzZSBwZWFrcyBsaW5rZWQgdG8gaGlnaGx5IHZhcmlhYmxlIGdlbmVzIHRvIGNvbXB1dGUgZ2VuZQphY3Rpdml0eSBzY29yZXMuCgpgYGB7cn0KaHZnX2xpc3QgPC0gcmVhZC50YWJsZSgianVweXRlcl9ub3RlYm9va3MvaHZnX2xpc3QiLCBzZXAgPSAiLCIpJHgKCgojIGdldCBSTkEgaW5kZXggb2YgaHZnCm1ldGFfcm5hIDwtIHJvd0RhdGEoZ2VuZV9leHByKSAlPiUgYXMuZGF0YS5mcmFtZSgpICU+JSBtdXRhdGUocm93X2luZGV4ID0gc2VxKG5yb3coLikpKQppZHggPC0gKG1ldGFfcm5hICU+JSBmaWx0ZXIobmFtZSAlaW4lIGh2Z19saXN0KSkkcm93X2luZGV4CgpleHByX3N1YiA8LSBleHByX21hdFtpZHgsIF0KYGBgCgpgYGB7cn0Kc2VhY2VsbHMgPC0gcmVhZF9jc3YoImp1cHl0ZXJfbm90ZWJvb2tzL1NFQUNlbGxfZmlsZXMvU0VBQ2VsbF9hc3NpZ25tZW50IikKYGBgCgoKYGBge3J9CmxpbmtzIDwtIHAyZyAlPiUgYXMuZGF0YS5mcmFtZSgpICU+JSAKICBmaWx0ZXIoQ29ycmVsYXRpb24gPiAwLjIpICU+JSAKICBmaWx0ZXIoaWR4Uk5BICVpbiUgaWR4KSAKCnN0b3BpZm5vdChhbGwobGlua3MkQ29ycmVsYXRpb24gPiAwKSkKYGBgCgoKCkNyZWF0ZSBhIHAyZyBsaW5rIG1hdHJpeAoKYGBge3J9CnAyZ19tYXQgPC0gc3BhcnNlTWF0cml4KGkgPSBsaW5rcyRpZHhSTkEsCiAgICAgICAgICAgICBqID0gbGlua3MkaWR4QVRBQywKICAgICAgICAgICAgIHg9IGxpbmtzJENvcnJlbGF0aW9uLCAKICAgICAgICAgICAgIGRpbXMgPSBjKGRpbShleHByX21hdClbMV0sCiAgICAgICAgICAgICBkaW0ocGVha19tYXQpWzFdKSkKCnJvd25hbWVzKHAyZ19tYXQpIDwtIHJvd0RhdGEoZ2VuZV9leHByKSRuYW1lCgoKcm93bmFtZXMocGVha19tYXQpIDwtIHNlcS5pbnQoZGltKHBlYWtfbWF0KVsxXSkKY29sbmFtZXMocDJnX21hdCkgPC0gc2VxLmludChkaW0ocGVha19tYXQpWzFdKQpgYGAKCgoKRmlsdGVyIGFuZCBwcmVwYXJlIHBlYWsgbWF0cml4IGFuZCBwMmcgbGlua3MgbWF0cml4OgoKYGBge3J9CiMgcmVtb3ZlIGNvbHVtbnMgb2YgcGVha3Mgd2hpY2ggYXJlIG5vdCBsaW5rZWQgdG8gYW55IHBlYWsKcDJnX21hdF9zdWIgPC0gcDJnX21hdFssIGNvbFN1bXMocDJnX21hdCkgIT0gMF0KIyB1c2Ugb25seSBoaWdobHkgdmFyaWFibGUgZ2VuZXMKcDJnX21hdF9zdWIgPC0gcDJnX21hdF9zdWJbaHZnX2xpc3QsIF0KIyByZW1vdmUgYW55IGdlbmVzIHdoaWNoIGFyZSBub3QgbGlua2VkIHRvIGFueSBwZWFrCnAyZ19tYXRfc3ViIDwtIHAyZ19tYXRfc3ViW3Jvd1N1bXMocDJnX21hdF9zdWIpICE9IDAsIF0Kc3RvcGlmbm90KGFsbChyb3duYW1lcyhwMmdfbWF0X3N1YikgJWluJSBodmdfbGlzdCkpCnN0b3BpZm5vdChhbnkoaXMubmEocDJnX21hdF9zdWIpID09IEZBTFNFKSkKCiMga2VlcCBvbmx5IHBlYWtzIHdoaWNoIGFyZSBsaW5rZWQgdG8gZ2VuZXMgaW4gdGhlIGFjY2Vzc2liaWxpdHkgbWF0cml4CnBlYWtfbWF0X3N1YiA8LSBwZWFrX21hdFtjb2xuYW1lcyhwMmdfbWF0X3N1YiksIF0Kc3RvcGlmbm90KHJvd25hbWVzKHBlYWtfbWF0X3N1YikgPT0gY29sbmFtZXMocDJnX21hdF9zdWIpKQojc3RvcGlmbm90KGFueShpcy5uYShwZWFrX21hdF9zdWIpID09IEZBTFNFKSkKc3RvcGlmbm90KGRpbShwZWFrX21hdF9zdWIpWzFdID09IGRpbShwMmdfbWF0X3N1YilbMl0pCgpleHByX21hdF9zdWIgPC0gZXhwcl9tYXRbYXMudmVjdG9yKHJvd25hbWVzKHAyZ19tYXRfc3ViKSksIF0KYGBgCgoKCgoKCiMjIyBGdW5jdGlvbiB0byBjb21wdXRlIGdlbmUgYWN0aXZpdHkgc2NvcmVzCgpgYGB7cn0KZ2VuZV9hY3Rpdml0eV9zY29yZXMgPC0gZnVuY3Rpb24ocGVha19tYXQsIHAyZ19tYXQpIHsKICAjcGVha19tYXRfc3Vic2V0IDwtIHBlYWtfbWF0W2NvbG5hbWVzKHAyZ19tYXQpLCBdCiAgIyBub3JtYWxpemUgdGhlIHAyZyBtYXRyaXggYnkgdGhlIHRvdGFsIG51bWJlciBvZiBwZWFrcyBsaW5rZWQgdG8gZWFjaCBnZW5lCiAgcDJnX21hdCA8LSBwMmdfbWF0IC8gcm93U3VtcyhwMmdfbWF0KQogIHByaW50KHBhc3RlMCgibm9ybWFsaXplZCB0aGUgcDJnIG1hdHJpeCIpKQogIHN0b3BpZm5vdChhbnkoaXMubmEocDJnX21hdCkpID09IEZBTFNFKQogICMgTm93IHdlIGNhbiBjb21wdXRlIGEgd2VpZ2h0ZWQgc3VtIG9mIHBlYWsyZ2VuZSBjb3JyZWxhdGlvbnMgZm9yIGVhY2gKICAjIHBlYWsgYW5kIGdlbmUKICBzY29yZXMgPC0gcDJnX21hdCAlKiUgcGVha19tYXQKICBwcmludChwYXN0ZTAoIkNvbXB1dGVkIHdlaWdodGVzIHN1bSBvZiBwZWFrcyBmb3IgZWFjaCBnZW5lIGFuZCBjZWxsIikpCiAgIyBjcmVhdGUgYSBkYXRhZnJhbWUgZm9yIGNvbXB1dGluZyB0aGUgbGluZWFyIG1vZGVsCiAgbGluZWFyX21vZGVsX2RmIDwtIGRhdGEuZnJhbWUoY2VsbCA9IGNvbG5hbWVzKHNjb3JlcyksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0b3RhbF9hY3Rpdml0eSA9IGNvbFN1bXMoc2NvcmVzKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRvdGFsX3NpdGVzID0gY29sU3VtcyhwZWFrX21hdCkpCiAgIyBjb21wdXRlIGEgbGluZWFyIG1vZGVsCiAgYWN0aXZpdHlfbW9kZWwgPC0gc3RhdHM6OmxtKGxvZyh0b3RhbF9hY3Rpdml0eSkgfiBsb2codG90YWxfc2l0ZXMpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgZGF0YSA9IGxpbmVhcl9tb2RlbF9kZikKICAjIGV4dHJhY3QgdGhlIGZpdHRlZCBtb2RlbAogIGxpbmVhcl9tb2RlbF9kZiRmaXR0ZWRfY3VydmUgPC0gZXhwKGFzLnZlY3RvcihwcmVkaWN0KGFjdGl2aXR5X21vZGVsLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0eXBlID0gInJlc3BvbnNlIikpKQogICMgY29tcHV0ZSBzaXplIGZhY3RvcnMgZnJvbSBmaXR0ZWQgbW9kZWwKICBzaXplX2ZhY3RvcnMgPC0gbWVhbihsaW5lYXJfbW9kZWxfZGYkZml0dGVkX2N1cnZlKSAvIGxpbmVhcl9tb2RlbF9kZiRmaXR0ZWRfY3VydmUKICAjIGNyZWF0ZSBkaWFnb25hbCBtYXRyaXggY29udGFpbmluZyB0aGUgc2l6ZSBmYWN0b3JzCiAgc2l6ZV9mYWN0b3JzX21hdCA8LSBNYXRyaXg6OkRpYWdvbmFsKHggPSBzaXplX2ZhY3RvcnMpCiAgI3Jvdy5uYW1lcyhzaXplX2ZhY3RvcnNfbWF0KSA8LSBsaW5lYXJfbW9kZWxfZGYkY2VsbAogICMgbm9ybWFsaXplIGJ5IGxpYnJhcnkgZGVwdGggc2l6ZSBmYWN0b3JzCiAgbm9ybV9zY29yZXMgPC0gTWF0cml4Ojp0KHNpemVfZmFjdG9yc19tYXQgJSolIE1hdHJpeDo6dChzY29yZXMpKQogIHByaW50KHBhc3RlMCgiTm9ybWFsaXplZCBmb3IgbGlicmFyeSBzaXplIikpCiAgIyBleHBvbmVudGlhdGUsIGJlY2F1c2UgUk5BIGNvdW50cyBhcmUgbG9nLW5vcm1hbGx5IGRpc3RyaWJ1dGVkCiAgbm9ybV9zY29yZXNAeCA8LSBwbWluKDFlOSwgZXhwKG5vcm1fc2NvcmVzQHgpIC0gMSkKICBwcmludChwYXN0ZTAoIkV4cG9uZW50aWF0ZWQgbWF0cml4IikpCiAgCiAgIyBmcmVlIHNvbWUgbWVtb3J5CiAgI3JtKHBlYWtfbWF0X3N1YnNldCkKICBybShhY3Rpdml0eV9tb2RlbCkKICBybShzY29yZXMpCiAgZ2MocmVzZXQgPSBUUlVFKQoKICAjIHNjYWxlIHdpdGggdG90YWwgYWN0aXZpdHkgc2NvcmVzIGFnYWluCiAgc2NhbGVfZmFjdG9ycyA8LSBNYXRyaXg6OkRpYWdvbmFsKHggPSAxL01hdHJpeDo6Y29sU3Vtcyhub3JtX3Njb3JlcykpCiAgcHJpbnQocGFzdGUwKCJEaXZpZGVkIGJ5IHRvdGFsIGFjdGl2aXR5IHRvIGdldCB2YWx1ZSBiZXR3ZWVuIHplcm8gYW5kIG9uZSIpKQogIAogIGZpbmFsX3Njb3JlcyA8LSBNYXRyaXg6OnQoc2NhbGVfZmFjdG9ycyAlKiUgTWF0cml4Ojp0KG5vcm1fc2NvcmVzKSkKCiAgcmV0dXJuKGZpbmFsX3Njb3JlcykKCn0KCmBgYAoKYGBge3J9CnAyZ19zY29yZXMgPC0gZ2VuZV9hY3Rpdml0eV9zY29yZXMocGVha19tYXRfc3ViLCBwMmdfbWF0X3N1YikKI3NhdmVSRFMocDJnX3Njb3JlcywgIkFyY2hSX3AyZ19iYXNlZF9nZW5lX2FjdGl2aXR5X3Njb3JlcyIpCmBgYAoKCgpUT0RPOiBBZGQgZnVuY3Rpb25zIGhlcmUsIGFkZCBBcmNoUiBhZ2dyZWdhdGVzIGFuZCBjb21wdXRlIGNvcnJlbGF0aW9ucyBhcyAKc2NhdHRlciAmIGRlbnNpdHkgcGxvdAoKIyBFeGFtcGxlIG9mIHAyZyBsaW5rcyB3aXRoaW4gMjUwa2IKCgpgYGB7cn0KY3BfbmFtZXMgPC0gY29sbmFtZXMoY29sRGF0YShnZW5lX2V4cHIpKQpjcF9uYW1lc1syMF0gPC0gImNlbGx0eXBlcyIKY29sbmFtZXMoY29sRGF0YShnZW5lX2V4cHIpKSA8LSBjcF9uYW1lcwoKI3Jvd25hbWVzKGV4cHJfbWF0KSA8LSByb3dEYXRhKGdlbmVfZXhwcikkbmFtZQpnZW5lcyA8LSBleHByX21hdFthcy52ZWN0b3Iocm93bmFtZXMocDJnX3Njb3JlcykpLCBdCgpzdG9waWZub3QoYW55KHJvd25hbWVzKGdlbmVzKSA9PSByb3duYW1lcyhwMmdfc2NvcmVzKSkpCgoKCiMgY3JlYXRlIG1hdHJpeCB0byBzdG9yZSBhZ2dyZWdhdGVzCmV4cHJfYWdnIDwtIG1hdHJpeChkYXRhID0gMCwgCiAgICAgICAgICAgICAgICAgICBucm93ID0gZGltKGdlbmVzKVsxXSwKICAgICAgICAgICAgICAgICAgIG5jb2wgPSBsZW5ndGgodW5pcXVlKGNvbERhdGEoZ2VuZV9leHByKSRjZWxsdHlwZXMpKSwKICAgICAgICAgICAgICAgICAgIGRpbW5hbWVzICA9IGxpc3Qocm93bmFtZXMocDJnX3Njb3JlcyksCiAgICAgICAgICAgICAgICAgICB1bmlxdWUoY29sRGF0YShnZW5lX2V4cHIpJGNlbGx0eXBlcykpKQoKCiMgZmlsbCBtYXRyaXgKZm9yIChjZWxsdHlwZSBpbiB1bmlxdWUoY29sRGF0YShnZW5lX2V4cHIpJGNlbGx0eXBlcykpewogIGJhcmNvZGVzIDwtIHJvd25hbWVzKGNvbERhdGEoZ2VuZV9leHByKSAlPiUgCiAgICAgICAgICAgICAgICAgICAgICAgICBhcy5kYXRhLmZyYW1lKCkgJT4lIAogICAgICAgICAgICAgICAgICAgICAgICAgZmlsdGVyKGNlbGx0eXBlcyA9PSBjZWxsdHlwZSkpCiAgZXhwcl9hZ2dbLCBjZWxsdHlwZV0gPC0gcm93U3VtcyhnZW5lc1ssIGJhcmNvZGVzXSkKfQoKCgpwMmdfc2NvcmVfYWdnIDwtIG1hdHJpeChkYXRhID0gMCwgCiAgICAgICAgICAgICAgICAgICAgICAgIG5yb3cgPSBkaW0ocDJnX3Njb3JlcylbMV0sCiAgICAgICAgICAgICAgICAgICAgICAgIG5jb2wgPSBsZW5ndGgodW5pcXVlKGNvbERhdGEoZ2VuZV9leHByKSRjZWxsdHlwZXMpKSwKICAgICAgICAgICAgICAgICAgICAgICAgZGltbmFtZXMgPSBsaXN0KHJvd25hbWVzKHAyZ19zY29yZXMpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdW5pcXVlKGNvbERhdGEoZ2VuZV9leHByKSRjZWxsdHlwZXMpKSkKCmZvciAoY2VsbHR5cGUgaW4gdW5pcXVlKGNvbERhdGEoZ2VuZV9leHByKSRjZWxsdHlwZXMpKXsKICBiYXJjb2RlcyA8LSByb3duYW1lcyhjb2xEYXRhKGdlbmVfZXhwcikgJT4lIAogICAgICAgICAgICAgICAgICAgICAgICAgYXMuZGF0YS5mcmFtZSgpICU+JSAKICAgICAgICAgICAgICAgICAgICAgICAgIGZpbHRlcihjZWxsdHlwZXMgPT0gY2VsbHR5cGUpKQogIHAyZ19zY29yZV9hZ2dbLCBjZWxsdHlwZV0gPC0gcm93U3VtcyhwMmdfc2NvcmVzWywgYmFyY29kZXNdKQp9CmBgYAoKCgoKQ29ycmVsYXRpb25zIGJldHdlZW4gYWdncmVnYXRlZCBnZW5lIGV4cHJlc3Npb24gYW5kIGFnZ3JlZ2F0ZWQgcDJnIHNjb3JlcyBmb3IgCmNlbGx0eXBlcy4KCgoKYGBge3J9CmNvcnJlbGF0aW9uc18yNTBrYiA9IGMoKQpmb3IgKGkgaW4gc2VxLmludChkaW0ocDJnX3Njb3JlX2FnZylbMV0pKXsKICByb3dhIDwtIGV4cHJfYWdnW2ksIF0KICByb3dhIDwtIHJvd2EgLSBtZWFuKHJvd2EpCiAgcm93YSA8LSByb3dhIC8gc2Qocm93YSkKICAKICByb3diIDwtIHAyZ19zY29yZV9hZ2dbaSwgXQogIHJvd2IgPC0gcm93YiAtIG1lYW4ocm93YikKICByb3diIDwtIHJvd2IgLyBzZChyb3diKQogIAogIGNvcnJfdmFsdWUgPSBtZWFuKHJvd2EgKiByb3diKQogIGNvcnJlbGF0aW9uc18yNTBrYiA8LSBjKGNvcnJlbGF0aW9uc18yNTBrYiwgY29ycl92YWx1ZSkKICAKfSAKbmFtZXMoY29ycmVsYXRpb25zXzI1MGtiKSA8LSByb3duYW1lcyhwMmdfc2NvcmVfYWdnKQoKcGxvdF8yNTBrYiA8LSBnZ3Bsb3QoKSArIGdlb21faGlzdG9ncmFtKGFlcyh4ID0gY29ycmVsYXRpb25zXzI1MGtiKSwgYmlucyA9IDIwMCwgZmlsbD0iIzY5YjNhMiIpICsKICBsYWJzKHRpdGxlID0gIkdlbmUgYWN0aXZpdHkgc2NvcmVzIGNvbXB1dGVkIGJhc2VkIG9uIHAyZyBsaW5rcyBvbiBlbnRpcmUgY2hyb21vc29tZSIpCnBsb3RfMjUwa2IKYGBgCgoKCiMgS05OIGNlbGwgYWdncmVnYXRlcyBmcm9tIEFyY2hSCgpUbyBjb21wdXRlIHBlYWstdG8tZ2VuZSBsaW5rcywgY2VsbCBhZ2dyZWdhdGVzIGFyZSBjcmVhdGVkIHRvIG92ZXJjb21lIHNwYXJzaXR5IGFzIGRlc2NyaWJlZCBhYm92ZS4gVG8gY3JlYXRlIGNlbGwgYWdncmVnYXRlcywgNTAwIGNlbGxzIGFyZSAKcmFuZG9tbHkgc2FtcGxlZCBmcm9tIGEgbG93LWRpbWVuc2lvbmFsIGVtYmVkZGluZywgZWcuIFBDQSBzcGFjZSBhbmQgdGhlIDUwIG5lYXJlc3QgbmVpZ2hib3JzIG9mIGVhY2ggY2VsbCBhcmUgZGV0ZXJtaW5lZC4gVGhpcyB3YXkgQXJjaFIgY3JlYXRlcyA1MDAgCmFnZ3JlZ2F0ZXMsIGVhY2ggY29uc2lzdGluZyBvZiA1MCBjZWxscy4gQmVsb3csIEkgdXNlZCB0aGVzZSBhZ2dyZWdhdGVzIGZvciBjb21wdXRpbmcgY29ycmVsYXRpb25zIGJldHdlZW4gZGlmZmVyZW50IGdlbmUgYWN0aXZpdHkgc2NvcmVzLCBob3dldmVyIHRoZSAKcmVzdWx0cyBkaWQgbm90IGxvb2sgdmVyeSB1c2FibGUsIHNpbmNlIGEgbG90IG9mIG5lZ2F0aXZlIGNvcnJlbGF0aW9ucyB3ZXJlIG9idGFpbmVkIHRoaXMgd2F5LiBUaGlzIG1pZ2h0IGJlIGR1ZSB0byB0aGVzZXMgYWdncmVnYXRlcyBub3QgYmVpbmcKcmVwcmVzZW50YXRpdmUsIHRvbyBzbWFsbCBvciB0b28gZmV3LiBJIGRlY2lkZWQgdG8gbm90IHVzZSB0aGVzZSBhZ2dyZWFnYXRlcywgYnV0IGluc3RhZWQgdXNlZCBTRUFDZWxscywgd2hpY2ggaXMgYSB0b29sIGZvciBjcmVhdGluZyAKY2VsbCBhZ2dyZWdhdGVzICgibWV0YWNlbGxzIikuIAoKYGBgI3tyfQpybmFfa25uIDwtIHJlYWRSRFMoIjExX2FkZGVkX1JpY2FyZHNfcGVha3MvUGVhazJHZW5lTGlua3Mvc2VSTkEtR3JvdXAtS05OLnJkcyIpCnJuYV9hZ2dfbWF0IDwtIGFzc2F5cyhybmFfa25uKVtbMV1dCnJvd25hbWVzKHJuYV9hZ2dfbWF0KSA8LSByb3dEYXRhKHJuYV9rbm4pJG5hbWUKCmNlbGxfYWdnX2xpc3QgPC0gbWV0YWRhdGEocm5hX2tubilbWzFdXQoKCmtubl9hZ2dyZWdhdGVzIDwtIGZ1bmN0aW9uKG1hdHJpeCwgY2VsbF9hZ2dfbGlzdCl7CiAgIyBlbXB0eSBtYXRyaXggdG8gc3RvcmUgYWdncmVnYXRlcwogIGFnZyA8LSBtYXRyaXgoZGF0YSA9IDAsCiAgICAgICAgICAgICAgICBucm93ID0gZGltKG1hdHJpeClbMV0sCiAgICAgICAgICAgICAgICBuY29sID0gbGVuZ3RoKGNlbGxfYWdnX2xpc3QpLAogICAgICAgICAgICAgICAgZGltbmFtZXMgPSBsaXN0KHJvd25hbWVzKG1hdHJpeCksIE5VTEwpKQogIAogIGZvciAoaSBpbiBzZXEuaW50KGxlbmd0aChjZWxsX2FnZ19saXN0KSkpIHsKICAgIGFnZ1ssIGldIDwtIHJvd1N1bXMobWF0cml4WywgY2VsbF9hZ2dfbGlzdFtbaV1dXSkKICB9CiAgcmV0dXJuKGFnZykKfQoKCnJuYV9hZ2cgPC0ga25uX2FnZ3JlZ2F0ZXMoZXhwcl9tYXRfc3ViLCBjZWxsX2FnZ19saXN0KQphZ2dfcDJnX2tubiA8LSBrbm5fYWdncmVnYXRlcyhwMmdfc2NvcmVzLCBjZWxsX2FnZ19saXN0KQoKYXJjaHJfa25uIDwtIGFyY2hyX3Njb3Jlc19tYXRbYXMudmVjdG9yKHJvd25hbWVzKGFnZ19wMmdfa25uKSksXQphZ2dfYXJjaHJfa25uIDwtIGtubl9hZ2dyZWdhdGVzKGFyY2hyX2tubiwgY2VsbF9hZ2dfbGlzdCkKYGBgCgoKYGBgI3tyfQphcmNocl9rbm4gPC0gcm93d2lzZV9jb3JyZWxhdGlvbnMocm5hX2FnZywgYWdnX2FyY2hyX2tubiwgIkFyY2hyIGdlbmUgYWN0aXZpdHkgc2NvcmVzIikKcDJnX2tubiA8LSByb3d3aXNlX2NvcnJlbGF0aW9ucyhybmFfYWdnLCBhZ2dfcDJnX2tubiwgIlBlYWstdG8tZ2VuZSBsaW5rcyBhY3Rpdml0eSBzY29yZXMiKQoKY293cGxvdDo6cGxvdF9ncmlkKGFyY2hyX2tubltbMl1dLCBwMmdfa25uW1syXV0sIG5jb2wgPSAyKQoKZ2dwbG90KCkgKyBnZW9tX2RlbnNpdHlfMmRfZmlsbGVkKGFlcyh4ID0gcDJnX2tubltbMV1dLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5ID0gYXJjaHJfa25uW1sxXV0pLCBhbHBoYSA9IC41KSArCiAgZ2VvbV9wb2ludChhZXMoeCA9IHAyZ19rbm5bWzFdXSwgeSA9IGFyY2hyX2tubltbMV1dKSkgKwogIGdlb21fbGluZShhZXMoeCA9IHAyZ19rbm5bWzFdXSwgeSA9IHAyZ19rbm5bWzFdXSksIGNvbCA9ICJyZWQiKQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJOb25lIikgCgpgYGAKCiMgRnVuY3Rpb25zCgojIyMgRnVuY3Rpb24gdG8gcHJlcCBwZWFrIGFjY2Vzc2liaWxpdHkgbWF0cml4LCBnZW5lIGV4cHJlc3Npb24gbWF0cml4IGFuZCBwMmctbGluayBtYXRyaXgKCmBgYCN7cn0KcHJlcF9wZWFrX3AyZyA8LSBmdW5jdGlvbihwZWFrX21hdCwgcDJnX21hdCwgaHZnX2xpc3QsIGV4cHJfbWF0KXsKICAjcm93bmFtZXMocGVha19tYXQpIDwtIHNlcS5pbnQoZGltKHBlYWtfbWF0KVsxXSkKICAjY29sbmFtZXMocDJnX21hdCkgPC0gc2VxLmludChkaW0ocDJnX21hdClbMl0pCiAgCiAgIyByZW1vdmUgY29sdW1ucyBvZiBwZWFrcyB3aGljaCBhcmUgbm90IGxpbmtlZCB0byBhbnkgcGVhawogIHAyZ19tYXRfc3ViIDwtIHAyZ19tYXRbLCBjb2xTdW1zKHAyZ19tYXQpICE9IDBdCiAgIyB1c2Ugb25seSBoaWdobHkgdmFyaWFibGUgZ2VuZXMKICBwMmdfbWF0X3N1YiA8LSBwMmdfbWF0X3N1YltodmdfbGlzdCwgXQogICMgcmVtb3ZlIGFueSBnZW5lcyB3aGljaCBhcmUgbm90IGxpbmtlZCB0byBhbnkgcGVhawogIHAyZ19tYXRfc3ViIDwtIHAyZ19tYXRfc3ViW3Jvd1N1bXMocDJnX21hdF9zdWIpICE9IDAsIF0KICBzdG9waWZub3QoYWxsKHJvd25hbWVzKHAyZ19tYXRfc3ViKSAlaW4lIGh2Z19saXN0KSkKICBzdG9waWZub3QoYW55KGlzLm5hKHAyZ19tYXRfc3ViKSA9PSBGQUxTRSkpCiAgCiAgIyBrZWVwIG9ubHkgcGVha3Mgd2hpY2ggYXJlIGxpbmtlZCB0byBnZW5lcyBpbiB0aGUgYWNjZXNzaWJpbGl0eSBtYXRyaXgKICBwZWFrX21hdF9zdWIgPC0gcGVha19tYXRbY29sbmFtZXMocDJnX21hdF9zdWIpLCBdCiAgc3RvcGlmbm90KHJvd25hbWVzKHBlYWtfbWF0X3N1YikgPT0gY29sbmFtZXMocDJnX21hdF9zdWIpKQogIHN0b3BpZm5vdChhbnkoaXMubmEocGVha19tYXRfc3ViKSA9PSBGQUxTRSkpCiAgc3RvcGlmbm90KGRpbShwZWFrX21hdF9zdWIpWzFdID09IGRpbShwMmdfbWF0X3N1YilbMl0pCiAgCiAgZXhwcl9tYXRfc3ViIDwtIGV4cHJfbWF0W2FzLnZlY3Rvcihyb3duYW1lcyhwMmdfbWF0X3N1YikpLCBdCiAgc3RvcGlmbm90KHJvd25hbWVzKGV4cHJfbWF0X3N1YikgPT0gcm93bmFtZXMocDJnX21hdF9zdWIpKQogIHJldHVybihsaXN0KHBlYWtfbWF0X3N1YiwgcDJnX21hdF9zdWIsIGV4cHJfbWF0X3N1YikpCn0KYGBgCgoKCgojIyMgRnVuY3Rpb24gdG8gY3JlYXRlIGFnZ3JlZ2F0ZSBtYXRyaWNlczoKCmBgYHtyfQojIHRoZSBkYXRhIG1hdHJpeCBuZWVkcyB0byBiZSBvZiBkaW1lbnNpb24gZmVhdHVyZXMgeCBjZWxscwojIHRoZSBjb2x1bW4gb2YgdGhlIGNvbERhdGEgb2YgdGhlIHNjZSBvYmplY3Qgd2hlcmUgY2VsbHR5cGVzIGFyZSBzdG9yZWQKIyBuZWVkcyB0byBiZSBjYWxsZWQgImNlbGx0eXBlcyIKY3JlYXRlX2NlbGx0eXBlX2FnZ3JlZ2F0ZXMgPC0gZnVuY3Rpb24oc2NlLCBkYXRhX21hdHJpeCwgY2VsbHR5cGVzKSB7CiAgI2NyZWF0ZSBlbXB0eSBtYXRyaXggdG8gc3RvcmUgYWdncmVnYXRlcwogIGFnZyA8LSBtYXRyaXgoZGF0YSA9IDAsCiAgICAgICAgICAgICAgICBucm93ID0gbnJvdyhkYXRhX21hdHJpeCksCiAgICAgICAgICAgICAgICBuY29sID0gbGVuZ3RoKGNlbGx0eXBlcyksCiAgICAgICAgICAgICAgICBkaW1uYW1lcyA9IGxpc3Qocm93bmFtZXMoZGF0YV9tYXRyaXgpLCBjZWxsdHlwZXMpKQogIAoKICBmb3IgKGNlbGx0eXBlIGluIGNlbGx0eXBlcykgewogICAgYmFyY29kZXMgPC0gcm93bmFtZXMoY29sRGF0YShzY2UpICU+JQogICAgICAgICAgICAgICAgICAgICAgICAgICBhcy5kYXRhLmZyYW1lKCkgJT4lCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbHRlcihjZWxsdHlwZXMgPT0gY2VsbHR5cGUpKQogICAgYWdnWywgY2VsbHR5cGVdIDwtIHJvd1N1bXMoZGF0YV9tYXRyaXhbLCBiYXJjb2Rlc10pCiAgfQogIHJldHVybihhZ2cpCn0KCgpjcmVhdGVfY2VsbHR5cGVfYWdncmVnYXRlc19wMmdfc2NvcmVzIDwtIGZ1bmN0aW9uKGdlbmVfZXhwcl9zY2UsIHAyZ19zY29yZV9tYXRyaXgsIGNlbGx0eXBlcykgewogICAgI2NyZWF0ZSBlbXB0eSBtYXRyaXggdG8gc3RvcmUgYWdncmVnYXRlcwogIGFnZyA8LSBtYXRyaXgoZGF0YSA9IDAsCiAgICAgICAgICAgICAgICBucm93ID0gbnJvdyhwMmdfc2NvcmVfbWF0cml4KSwKICAgICAgICAgICAgICAgIG5jb2wgPSBsZW5ndGgoY2VsbHR5cGVzKSwKICAgICAgICAgICAgICAgIGRpbW5hbWVzID0gbGlzdChyb3duYW1lcyhwMmdfc2NvcmVfbWF0cml4KSwgY2VsbHR5cGVzKSkKICAKCiAgZm9yIChjZWxsdHlwZSBpbiBjZWxsdHlwZXMpIHsKICAgIGJhcmNvZGVzIDwtIHJvd25hbWVzKGNvbERhdGEoZ2VuZV9leHByX3NjZSkgJT4lCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGFzLmRhdGEuZnJhbWUoKSAlPiUKICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsdGVyKGNlbGx0eXBlcyA9PSBjZWxsdHlwZSkpCiAgICBhZ2dbLCBjZWxsdHlwZV0gPC0gcm93U3VtcyhwMmdfc2NvcmVfbWF0cml4WywgYmFyY29kZXNdKQogIH0KICByZXR1cm4oYWdnKQp9CgoKY3JlYXRlX3NlYWNlbGxfYWdncmVnYXRlcyA8LSBmdW5jdGlvbihkYXRhX21hdHJpeCwgc2VhY2VsbHNfZGYpewogIGFnZyA8LSBtYXRyaXgoZGF0YSA9IDAsCiAgICAgICAgICAgICAgICBucm93ID0gbnJvdyhkYXRhX21hdHJpeCksCiAgICAgICAgICAgICAgICBuY29sID0gbGVuZ3RoKHVuaXF1ZShzZWFjZWxsc19kZiRTRUFDZWxsKSksCiAgICAgICAgICAgICAgICBkaW1uYW1lcyA9IGxpc3Qocm93bmFtZXMoZGF0YV9tYXRyaXgpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB1bmlxdWUoc2VhY2VsbHNfZGYkU0VBQ2VsbCkpKQogICNzdG9waWZub3QobnJvdyhhZ2cpID09IG5yb3coZGF0YV9tYXRyaXgpKQogIGZvciAoc2VhY2VsbCBpbiB1bmlxdWUoc2VhY2VsbHNfZGYkU0VBQ2VsbCkpewogICAgI3ByaW50KHNlYWNlbGwpCiAgICBiYXJjb2RlcyA8LSAoc2VhY2VsbHNfZGYgJT4lIGZpbHRlcihTRUFDZWxsID09IHNlYWNlbGwpKSRpbmRleAogICAgI3ByaW50KGJhcmNvZGVzKQogICAgaWYgKGxlbmd0aChiYXJjb2RlcykgPT0gMSl7CiAgICAgIGFnZ1ssIHNlYWNlbGxdIDwtIGRhdGFfbWF0cml4WywgYmFyY29kZXNdCiAgICB9IGVsc2V7CiAgICAgIGFnZ1ssIHNlYWNlbGxdIDwtIHJvd1N1bXMoZGF0YV9tYXRyaXhbLCBiYXJjb2Rlc10pCiAgICB9CiAgfQogIHJldHVybihhZ2cpCn0KCmBgYAoKCiMjIyBGdW5jdGlvbiB0byBjb21wdXRlIHJvdy13aXNlIGNvcnJlbGF0aW9ucyBiZXR3ZWVuIHR3byBtYXRyaWNlczoKCmBgYHtyfQpyb3d3aXNlX2NvcnJlbGF0aW9ucyA8LSBmdW5jdGlvbihNYXRyaXhBLCBNYXRyaXhCLCBuYW1lKSB7CiAgaW50ZXJzZWN0X2dlbmVzIDwtIGludGVyc2VjdChyb3duYW1lcyhNYXRyaXhBKSwgcm93bmFtZXMoTWF0cml4QikpCiAgTWF0cml4QSA8LSBNYXRyaXhBW2ludGVyc2VjdF9nZW5lcywgXQogIE1hdHJpeEIgPC0gTWF0cml4QltpbnRlcnNlY3RfZ2VuZXMsIF0KICBjb3JyZWxhdGlvbnMgPC0gYygpCiAgZm9yIChpIGluIHNlcS5pbnQoZGltKE1hdHJpeEEpWzFdKSkgewogICAgcm93QSA8LSBNYXRyaXhBW2ksIF0KICAgIHJvd0EgPC0gcm93QSAtIG1lYW4ocm93QSkKICAgIGlmIChzZChyb3dBKSAhPSAwKSB7CiAgICAgIHJvd0EgPC0gcm93QSAvIHNkKHJvd0EpCiAgICB9CiAgCiAgICByb3dCIDwtIE1hdHJpeEJbaSwgXQogICAgcm93QiA8LSByb3dCIC0gbWVhbihyb3dCKQogICAgaWYgKHNkKHJvd0IpICE9IDApewogICAgICByb3dCIDwtIHJvd0IgLyBzZChyb3dCKQogICAgfQogICAgCiAgICBjb3JyX3ZhbHVlIDwtIG1lYW4ocm93QSAqIHJvd0IpCiAgICBjb3JyZWxhdGlvbnMgPC0gYyhjb3JyZWxhdGlvbnMsIGNvcnJfdmFsdWUpCiAgfQogIG5hbWVzKGNvcnJlbGF0aW9ucykgPC0gcm93bmFtZXMoTWF0cml4QSkKICBwbG90IDwtIGdncGxvdCgpICsgZ2VvbV9oaXN0b2dyYW0oYWVzKHggPSBjb3JyZWxhdGlvbnMpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYmlucyA9IDIwMCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbGw9IiM2OWIzYTIiKSArIGxhYnModGl0bGUgPSBwYXN0ZTAobmFtZSkpCiAgcmV0dXJuKGxpc3QoY29ycmVsYXRpb25zLCBwbG90KSkKfQpgYGAKCgojIyMgRnVuY3Rpb24gZm9yIEFyY2hSIEtOTiBhZ2dyZWdhdGVzCgpgYGB7cn0KIyBGdW5jdGlvbiB0byBjb21wdXRlIGFnZ3JlZ2F0ZXMgd2l0aCBrbm4gZnJvbSBBcmNoUgprbm5fYWdncmVnYXRlcyA8LSBmdW5jdGlvbihtYXRyaXgsIGNlbGxfYWdnX2xpc3QpewogICMgZW1wdHkgbWF0cml4IHRvIHN0b3JlIGFnZ3JlZ2F0ZXMKICBhZ2cgPC0gbWF0cml4KGRhdGEgPSAwLAogICAgICAgICAgICAgICAgbnJvdyA9IGRpbShtYXRyaXgpWzFdLAogICAgICAgICAgICAgICAgbmNvbCA9IGxlbmd0aChjZWxsX2FnZ19saXN0KSwKICAgICAgICAgICAgICAgIGRpbW5hbWVzID0gbGlzdChyb3duYW1lcyhtYXRyaXgpLCBOVUxMKSkKICAKICBmb3IgKGkgaW4gc2VxLmludChsZW5ndGgoY2VsbF9hZ2dfbGlzdCkpKSB7CiAgICBhZ2dbLCBpXSA8LSByb3dTdW1zKG1hdHJpeFssIGNlbGxfYWdnX2xpc3RbW2ldXV0pCiAgfQogIHJldHVybihhZ2cpCn0KCmBgYAoKIyBDZWxsdHlwZSBhZ2dyZWdhdGVzCgojIyBBcmNoUiBnZW5lIGFjdGl2aXR5IHNjb3JlcwoKVG8gY29tcHV0ZSB0aGUgY29ycmVsYXRpb25zIGJldHdlZW4gZ2VuZSBleHByZXNzaW9uIGFuZCBBcmNoUiBnZW5lIGFjdGl2aXR5IApzY29yZXMgSSBmaXJzdCBhZ2dyZWdhdGVkIGNlbGxzIGFjY29yZGluZyB0byBjZWxsdHlwZXMgdG8gY29tcHV0ZSAKY29ycmVsYXRpb25zLiBBcyBjYW4gYmUgc2VlbiBpbiB0aGUgcGxvdCBiZWxvdywgdGhpcyB5aWVsZHMgdmVyeSBoaWdoIApjb3JyZWxhdGlvbiB2YWx1ZXMuIFRoaXMgaXMgYXMgZXhwZWN0ZWQsIHNpbmNlIGluIFtAR3JhbmphXSB0aGUgYXV0aG9ycyAKY29tcGFyZWQgNTIgZGlmZmVyZW50IHdheXMgb2YgY29tcHV0aW5nIGdlbmUgYWN0aXZpdHkgc2NvcmVzIGZyb20gQVRBQy1zZXEgCmRhdGEgYW5kIGZvdW5kIHRoZWlyIG1ldGhvZCB0byBiZSB0aGUgYmVzdCBvbmUuIAoKCmBgYHtyLCBmaWcud2lkdGg9OH0KYXJjaHJfc2NvcmVzX3N1YiA8LSBhcmNocl9zY29yZXNfbWF0W2FzLnZlY3Rvcihyb3duYW1lcyhleHByX21hdF9zdWIpKSwgXQoKbmFtZSA8LSAiQXJjaFJfc2NvcmVzLCBDZWxsdHlwZSBhZ2dyZWdhdGVzIgoKYXJjaHJfc2NvcmVzX2FnZyA8LSBjcmVhdGVfY2VsbHR5cGVfYWdncmVnYXRlcyhhcmNocl9zY29yZXMsIGFyY2hyX3Njb3Jlc19zdWIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHVuaXF1ZShjb2xEYXRhKGFyY2hyX3Njb3JlcykkY2VsbHR5cGVzKSkKc3RvcGlmbm90KGFueShpcy5uYShhcmNocl9zY29yZXNfYWdnKSkgPT0gRkFMU0UpCgpjb3JycyA8LSByb3d3aXNlX2NvcnJlbGF0aW9ucyhleHByX2FnZywgYXJjaHJfc2NvcmVzX2FnZywgbmFtZSkKYXJjaHJfY29yciA8LSBjb3Jyc1sxXQpjb3dwbG90OjpwbG90X2dyaWQocGxvdF8yNTBrYiArIGxhYnModGl0bGUgPSAiUDJnLWxpbmtzIGFjdGl2aXR5IHNjb3JlcywgQ2VsbHR5cGUgYWdncmVnYXRlcyIpLCBjb3Jyc1tbMl1dLCBuY29sID0gMikKYGBgCgoKCmBgYHtyLCBmaWcuaGVpZ2h0PTUsIGZpZy53aWR0aD04fQojLCBmaWcud2lkdGggPSA1LCBmaWcuaGVpZ2h0PTV9CmdncGxvdCgpICsgI2dlb21fZGVuc2l0eTJkX2ZpbGxlZChhZXMoeCA9IGNvcnJlbGF0aW9uc18yNTBrYiwgeSA9IGNvcnJzWzFdKSkgIysKICBnZW9tX3BvaW50KGFlcyh4ID0gY29ycmVsYXRpb25zXzI1MGtiLCB5ID0gY29ycnNbWzFdXSkpICsKICBnZW9tX2RlbnNpdHlfMmRfZmlsbGVkKGFlcyh4ID0gY29ycmVsYXRpb25zXzI1MGtiLCB5ID0gY29ycnNbWzFdXSksIGFscGhhID0gMC41KSArCiAgZ2VvbV9saW5lKGFlcyh4ID0gIGNvcnJzW1sxXV0sICBjb3Jyc1tbMV1dKSwgY29sb3IgPSAicmVkIikgKwogIGxhYnMoeCA9ICJDb3JyZWxhdGlvbiBnZW5lIGV4cHJlc3Npb24gYW5kIHAyZyBhY3Rpdml0eSBzY29yZXMiLAogICAgICAgeSA9ICJDb3JyZWxhdGlvbiBnZW5lIGV4cHJlc3Npb24gYW5kIEFyY2hSIGdlbmUgYWN0aXZpdHkgc2NvcmVzIiwKICAgICAgIHRpdGxlID0gIkNlbGx0eXBlIGFnZ3JlZ2F0ZXMiKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIk5vbmUiKQoKCiMgZ2dwbG90KCkgKyBnZW9tX3BvaW50KGFlcyh4ID0gYXJjaHJfc2NvcmVzX3N1YlsiSGJhLWExIixdLCB5ID0gcDJnX3Njb3Jlc1siSGJhLWExIixdKSkKIyBnZ3Bsb3QoKSArIGdlb21fcG9pbnQoYWVzKHggPSBhcmNocl9zY29yZXNfc3ViWyJHYXRhNiIsXSwgeSA9IHAyZ19zY29yZXNbIkhiYS1hMSIsXSkpCgpgYGAKCiMgU0VBQ2VsbCBhZ2dyZWdhdGVzCgpJbnN0ZWFkIG9mIHVzaW5nIGNlbGx0eXBlIGFnZ3JlZ2F0ZXMgYXMgYWJvdmUsIGFub3RoZXIgb3B0aW9uIGlzIHRvCnVzZSBTRUFDZWxscyBhcyBkZXNjcmliZWQgaW4gW0BQZXJzYWQyMDIyXS4gVGhlc2Ugd2VyZSBjb21wdXRlZCB1c2luZwpQeXRob24gYW5kIHRoZSByZXN1bHRpbmcgY2VsbCBhZ2dyZWdhdGVzICgibWV0YWNlbGxzIikgYXJlIHVzZWQgZm9yCmFnZ3JlZ2F0aW5nIGdlbmUgZXhwcmVzc2lvbiBhbmQgZ2VuZSBhY3Rpdml0eSBzY29yZXMgYmVsb3cuIFRoZSBjb3JyZWxhdGlvbnMKd2hlbiB1c2luZyBTRUFDZWxscyBhcmUgbXVjaCBoaWdoZXIgdGhhbiB0aGUgY29ycmVsYXRpb25zIG9idGFpbmVkIHVzaW5nIAp0aGUgQXJjaFIgY2VsbCBhZ2dyZWdhdGVzLiBGb3IgdGhpcyByZWFzb24gSSB3aWxsIHVzZSBTRUFDZWxscyBmb3IgY29tcHV0aW5nCmNvcnJlbGF0aW9ucyBpbiB0aGUgZm9sbG93aW5nIHN0ZXBzLiAKCmBgYHtyLCBmaWcud2lkdGggPSA4LCBmaWcuaGVpZ2h0PTV9CgoKc2VhY2VsbHMgPC0gc2VhY2VsbHMgJT4lIGZpbHRlcihpbmRleCAlaW4lIGNvbG5hbWVzKGV4cHJfbWF0KSkKCnN0b3BpZm5vdChucm93KHAyZ19zY29yZXMpID09IG5yb3coZXhwcl9tYXRfc3ViKSkKCnNlYWNlbGxfcDJnX2FnZyA8LSBjcmVhdGVfc2VhY2VsbF9hZ2dyZWdhdGVzKHAyZ19zY29yZXMsIHNlYWNlbGxzKQpzZWFjZWxsX3JuYV9hZ2cgIDwtIGNyZWF0ZV9zZWFjZWxsX2FnZ3JlZ2F0ZXMoZXhwcl9tYXRfc3ViLCBzZWFjZWxscykKc2VhY2VsbF9hcmNocl9hZ2cgPC0gY3JlYXRlX3NlYWNlbGxfYWdncmVnYXRlcyhhcmNocl9zY29yZXNfc3ViLCBzZWFjZWxscykKCgpzZWFjZWxsX2NvcnJfcDJnIDwtIHJvd3dpc2VfY29ycmVsYXRpb25zKHNlYWNlbGxfcm5hX2FnZyAsIHNlYWNlbGxfcDJnX2FnZywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlAyZyBsaW5rcyBvZiBlbnRpcmUgY2hyb21vc29tZSwgU0VBY2VsbHMiICkKCnNlYWNlbGxfY29ycl9hcmNociA8LSByb3d3aXNlX2NvcnJlbGF0aW9ucyhzZWFjZWxsX3JuYV9hZ2csIHNlYWNlbGxfYXJjaHJfYWdnLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJBcmNoUiBnZW5lIGFjdGl2aXR5IHNjb3JlcywgU0VBY2VsbHMiKQoKY293cGxvdDo6cGxvdF9ncmlkKHNlYWNlbGxfY29ycl9wMmdbWzJdXSwgc2VhY2VsbF9jb3JyX2FyY2hyW1syXV0sIG5jb2wgPSAyKQoKCmdncGxvdCgpICsgI2dlb21fZGVuc2l0eTJkX2ZpbGxlZChhZXMoeCA9IGNvcnJlbGF0aW9uc18yNTBrYiwgeSA9IGNvcnJzWzFdKSkgIysKICBnZW9tX3BvaW50KGFlcyh4ID0gc2VhY2VsbF9jb3JyX3AyZ1tbMV1dLCB5ID0gc2VhY2VsbF9jb3JyX2FyY2hyW1sxXV0pKSArCiAgZ2VvbV9kZW5zaXR5XzJkX2ZpbGxlZChhZXMoeCA9IHNlYWNlbGxfY29ycl9wMmdbWzFdXSwgeSA9IHNlYWNlbGxfY29ycl9hcmNocltbMV1dKSwgYWxwaGEgPSAwLjUpICsKICBnZW9tX2xpbmUoYWVzKHggPSBzZWFjZWxsX2NvcnJfYXJjaHJbWzFdXSwgeSA9IHNlYWNlbGxfY29ycl9hcmNocltbMV1dKSwgY29sb3IgPSAicmVkIiApICArCiAgbGFicyh4ID0gIkNvcnJlbGF0aW9uIGdlbmUgZXhwcmVzc2lvbiBhbmQgcDJnIGFjdGl2aXR5IHNjb3JlcyIsCiAgICAgICB5ID0gIkNvcnJlbGF0aW9uIGdlbmUgZXhwcmVzc2lvbiBhbmQgQXJjaFIgZ2VuZSBhY3Rpdml0eSBzY29yZXMiLAogICAgICAgdGl0bGUgPSAiU0VBQ2VsbHMiKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIk5vbmUiKQoKYGBgCgoKCgojIERpc3RhbmNlIHdlaWdodHMKClVzaW5nIEFyY2hSIFtAR3JhbmphXSBJIGNvbXB1dGVkIHBlYWstdG8tZ2VuZSBsaW5rcyBhY3Jvc3MgdGhlIGVudGlyZSAKY2hyb21vc29tZSwgYnV0IG5vdCBiZXR3ZWVuIGNocm9tc29tZXMuIFRoaXMgbWVhbnMgdGhhdCBhIGxvdCBvZiAKY29ycmVsYXRpb25zIGFyZSBmb3VuZCBiZXR3ZWVuIHBlYWtzIHZlcnkgZmFyIGF3YXkgZnJvbSB0aGUgcHJvbW90ZXIvZ2VuZQp0aGV5IGFyZSBsaW5rZWQgdG8uIEV2ZW4gdGhvdWdoIHRoZXNlIGNvcnJlbGF0aW9ucyBjYW4gYmUgcXVpdGUgaGlnaCBhbmQgaW50ZXJhY3Rpb25zCmJldHdlZW4gZW5oYW5jZXJzIGFuZCBwcm9tb3RlcnMgY2FuIG9jY3VyIG92ZXIgbWVnYWJhc2UgZGlzdGFuY2VzLCBhIHJlYWwKYmlvbG9naWNhbCBpbnRlcmFjdGlvbiBiZWNvbWVzIGxlc3MgbGlrZWx5IHRoZSBsYXJnZXIgdGhlIGRpc3RhbmNlIGlzLiBUaGVyZWZvcmUsIHNpbmNlIHdlciBhcmUgaW50ZXJlc3RlZCBpbiBiaW9sb2dpY2FsbHkgcmVsZXZhbnQgYW5kIG5vdApzcHVyaW91cyBjb3JyZWxhdGlvbnMuIFRoZXJlZm9yZSwgYXMgc3VnZ2VzdGVkIGJ5IFtAR3JhbmphXSwgSSBhZGRlZCAKZGlzdGFuY2Ugd2VpZ2h0cywgc3VjaCB0aGF0IGZhcnRoZXIgYXdheSBwZWFrcyBsaW5rZWQgdG8gYSBnZW5lIGNvbnRyaWJ1dGUKbGVzcyB0byB0aGUgZ2VuZSBhY3Rpdml0eSBzY29yZSBvZiB0aGlzIHBhcnRpY3VsYXIgZ2VuZS4gCgpIZXJlLCBJIHVzZWQgYSBkaXN0YW5jZSBkZWNheSBmcm9tIHRoZSBUU1MsIGNvbXB1dGVkIGFzIGZvbGxvd3M6Cgokd2VpZ2h0ID0gZV57LShhYnMoZGlzdFRTUy9jKSl9JCB3aXRoICRjJCBiZWluZyBhIGNvbnN0YW50IGRldGVybWluaW5nIHRoZSBleHBvbmVudGlhbApkZWNheSByYXRlIG9mIHRoZSBkaXN0YW5jZSB3ZWlnaHRzLiBCZWxvdyBJIHRyaWVkIGRpZmZlcmVudCByYXRlcyB0byBiZXR0ZXIgdW5kZXJzdGFuZCAKd2hldGhlciB3ZSBjYW4gaW1wcm92ZSB0aGUgZ2VuZSBhY3Rpdml0eSBzY29yZXMgYnkgZ2l2aW5nIGEgaGlnaGVyIHdlaWdodCB0bwpjbG9zZSBwZWFrcyB0aGFuIHRvIGZhciBhd2F5IHBlYWtzLiBBcyBjYW4gYmUgc2VlbiBiZWxvdyB0aGlzIGRpZCBub3QgaW1wcm92ZSwgdGhlCnNjb3JlcywgYnV0IHJhdGhlciB0aGUgc2NvcmVzIGJlY2FtZSB3b3JzZSwgd2hpY2ggaXMgcHJvYmFibHkgZHVlIHRvIHRoZSBmYWN0IHRoYXQKbW9zdCBjb3JyZWxhdGlvbiB2YWx1ZXMgd2lsbCBnZXQgdmVyeSBzbWFsbCB3ZWlnaHRzIHRoaXMgd2F5IGFuZCBtb3N0IHBlYWtzIGxpbmtlZAp3aXRoIGEgZ2VuZSwgZXZlbiBpZiB0aGUgY29ycmVsYXRpb24gdmFsdWUgaXMgaGlnaCwgd2lsbCBub3QgY29udHJpYnV0ZSB0byB0aGUgZ2VuZQphY3Rpdml0eSBzY29yZSBhbnltb3JlLgoKKipDYXJlZnVsOiBUaGUgcDJnIGlua3MgaW4gQXJjaFIgYXJlIGNvbXB1dGVkIGZvciBwZWFrIGFuZCBnZW5lIHBhaXJzIHdoaWNoIGFyZSAKd2l0aGluIGEgY2VydGFpbiBkaXN0YW5jZSBmcm9tIGVhY2ggb3RoZXIuIEhvd2V2ZXIsIG5vdCB0aGUgcmVhbCBUU1Mgb2YgYSBnZW5lIGlzCnVzZWQgZm9yIHRoaXMsIGJ1dCByYXRlciB0aGUgZGlzdGFuY2UgYmV0d2VlbiBzdGFydCBjb29yZGluYXRlCm9mIGEgZ2VuZSBhbmQgcGVhayBzdGFydCBjb29yZGluYXRlLCBub3QgdGFraW5nIGludG8gY29uc2lkZXJhdGlvbiB0aGUgc3RyYW5kIGRpcmVjdGlvbmFsaXR5LgoKCiEhISEhISBDaGVjayBhZ2FpbiEgQmVjYXVzZSBoZXJlIHNvbWV0aGluZyBpcyB3cm9uZyB3aXRoIHRoZSB3YXkgSSBjb21wdXRlIHRoZSBkaXN0YW5jZSB3ZWlnaHRzISBTb21ldGltZXMgSSBuZWVkIHRvIHVzZSB0aGUgc3RhcnQgY29vcmRpbmF0ZSBpbnN0ZWFkIG9mIHRoZSBlbmQgY29vcmRpbmF0ZS4gVHJ5IGFsd2F5cyB1c2luZyB0aGUgZ2VuZSBzdGFydCBjb29yZGluYXRlIGluc3RlYWQgb2Ygc3dhcHBpbmcgc3RhcnQKYW5kIGVuZCBjb29yZGluYXRlcyBpbiB0aGUgZGF0YWZyYW1lLiBNYXliZSB0aGlzIGlzIGRvbmUgYXV0b21hdGljYWxsIHdoZW4gY29udmVydGVkIAp0byBkYXRhZnJhbWU/CgojIyMgRnVuY3Rpb24gdG8gY29tcHV0ZSBkaXN0YW5jZS13ZWlnaHRlZCBnZW5lIGFjdGl2aXR5IHNjb3JlcyBmcm9tIHAyZyBsaW5rcwoKYGBge3J9CiMgQXMgaW5wdXQgZm9yIHRoaXMgZnVuY3Rpb24gaXQgaXMgYmVzdCB0byB1c2Ugb25seSB0aGUgbW9zdCBoaWdobHkgdmFyaWFibGUgZ2VuZXMKZGlzdGFuY193ZWlnaHRlZF9nZW5lX2FjdGl2aXR5X3Njb3JlcyA8LSBmdW5jdGlvbihwMmdfbWF0X3N1YiwgZ2VuZU1vZGVsID0gImV4cCgtZGlzdGFuY2UvNTAwMCkiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB3ZWlnaHQgPSA1MDAwMCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwZWFrX21hdCwgbGlua3MsIHAyZ19vcmlnaW5hbCwgZ2VuZV9leHByKXsKICBhdGFjX2dyYW5nZXMgPC0gbWV0YWRhdGEocDJnX29yaWdpbmFsKVtbMV1dCiAgI3JuYV9ncmFuZ2VzIDwtIG1ldGFkYXRhKHAyZ19vcmlnaW5hbClbWzJdXQogIGdlbmVfYW5ubyA8LSByb3dEYXRhKGdlbmVfZXhwcikKICAKICAjIGNyZWF0ZSBnZW5lIGFubm90YXRpb25zIHdpdGggc3RhcnQgY29vcmRpbmF0ZSBvZiBlYWNoIGdlbmUKICAjIHN1YnNldCB0byBjb250YWluIG9ubHkgZ2VuZXMgd2hpY2ggYXJlIGluY2x1ZGVkIGluIG91ciBwZWFrMmdlbmUgbWF0cml4CiAgZ2VuZV9hbm5vIDwtIGdlbmVfYW5ubyAlPiUgYXMuZGF0YS5mcmFtZSgpICU+JQogICAgbXV0YXRlKGlkeFJOQSA9IHNlcShucm93KC4pKSkgJT4lIAogICAgZmlsdGVyKG5hbWUgJWluJSByb3duYW1lcyhwMmdfbWF0X3N1YikpICU+JQogICAgbXV0YXRlKHN0cmFuZCA9IGlmZWxzZShzdHJhbmQgPT0gMSwgIisiLCAiLSIpKSAlPiUgCiAgICBtdXRhdGUoc3RhcnRfY29vcmQgPSBpZmVsc2Uoc3RyYW5kID09ICIrIiwgc3RhcnQsIGVuZCkpICU+JSAKICAgIHJlbmFtZShnZW5lID0gbmFtZSkgIyU+JSBHUmFuZ2VzKCkKCiAgIyBzdWJzZXQgYXRhYyBncmFuZ2VzICYgZ2V0IG1pZGRsZSBvZiBlYWNoIHBlYWsKICBwb3NfYXRhY19ncmFuZ2VzIDwtIGF0YWNfZ3JhbmdlcyAgJT4lIAogICAgYXMuZGF0YS5mcmFtZSgpICU+JQogICAgbXV0YXRlKGlkeEFUQUMgPSBzZXEobnJvdyguKSkpICU+JSAKICAgICMgZ3JvdXBfYnkoc2VxbmFtZXMpICU+JQogICAgIyBtdXRhdGUoaWR4ID0gc2VxX2Fsb25nKHNlcW5hbWVzKSkgJT4lIAogICAgIyB1bmdyb3VwICU+JQogICAgI3RpZHlyOjp1bml0ZShjaHJfaWR4LCBzZXFuYW1lcywgaWR4LCByZW1vdmUgPSBGQUxTRSwgc2VwID0gIl8iKSAlPiUgCiAgICBmaWx0ZXIoaWR4QVRBQyAlaW4lIGNvbG5hbWVzKHAyZ19tYXRfc3ViKSkgJT4lIAogICAgbXV0YXRlKG1pZGRsZSA9IHN0YXJ0ICsgMzAwKSAjJT4lIEdSYW5nZXMoKSAKICAKICAjVE9ETzogRmlsdGVyIGZvciBnZW5lcyEKICBzdG9waWZub3QobGVuZ3RoKHVuaXF1ZShsaW5rcyRpZHhBVEFDKSkgPT0gZGltKHBvc19hdGFjX2dyYW5nZXMpW1sxXV0pCiAgc3RvcGlmbm90KGxlbmd0aCh1bmlxdWUobGlua3MkaWR4Uk5BKSkgPT0gZGltKGdlbmVfYW5ubylbWzFdXSkKICAjcDJnX2ZpbHQgPC0gcDJnX29yaWdpbmFsICU+JSBhcy5kYXRhLmZyYW1lKCkgJT4lIGZpbHRlcihnZW5lICVpbiUgcm93bmFtZXMocDJnX21hdCkpCiAgCiAgCiAgIyBjb21iaW5lIHRoZSB0aHJlZSBkYXRhZnJhbWVzCiAgcDJnX2pvaW4gPC0gbGVmdF9qb2luKGxpbmtzLCBhcy5kYXRhLmZyYW1lKHBvc19hdGFjX2dyYW5nZXMpLAogICAgICAgICAgICAgICAgICAgICAgICBieSA9ICJpZHhBVEFDIikKICBwMmdfam9pbiA8LSBsZWZ0X2pvaW4ocDJnX2pvaW4sIGFzLmRhdGEuZnJhbWUoZ2VuZV9hbm5vKSwKICAgICAgICAgICAgICAgICAgICAgICAgYnkgPSAiaWR4Uk5BIiwgc3VmZml4ID0gYygiLmF0YWMiLCAiLnJuYSIpKQoKICAjIGNvbXB1dGUgZGlzdGFuY2UgYW5kIGRpc3RhbmNlIHdlaWdodHMgCiAgcDJnX2pvaW4gPC0gcDJnX2pvaW4gJT4lIAogICAgbXV0YXRlKGRpc3RhbmNlID0gYWJzKHN0YXJ0X2Nvb3JkIC0gbWlkZGxlKSkgJT4lCiAgICBtdXRhdGUoZGlzdGFuY2Vfd2VpZ2h0ID0gZXZhbChwYXJzZSh0ZXh0PWdlbmVNb2RlbCkpKQogIAogIAogICMgY3JlYXRlIGRpc3RhbmNlIHdlaWdodCBtYXRyaXgKICBwMmdfZHcgPC0gc3BhcnNlTWF0cml4KGkgPSBwMmdfam9pbiRpZHhSTkEsCiAgICAgICAgICAgICAgICAgICAgICAgICBqID0gcDJnX2pvaW4kaWR4QVRBQywKICAgICAgICAgICAgICAgICAgICAgICAgIHggPSBwMmdfam9pbiRkaXN0YW5jZV93ZWlnaHQsCiAgICAgICAgICAgICAgICAgICAgICAgICBkaW1zID0gYyhkaW0oYXNzYXlzKGdlbmVfZXhwcilbWzFdXSlbMV0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkaW0ocGVha19tYXQpWzFdKSwKICAgICAgICAgICAgICAgICAgICAgICAgIGRpbW5hbWVzID0gbGlzdChyb3dEYXRhKGdlbmVfZXhwcikkbmFtZSAsIAogICAgICAgICAgICAgICAgICAgICAgICAgc2VxLmludChkaW0ocGVha19tYXQpWzFdKSkpCgoKICBwMmdfZHcgPC0gcDJnX2R3W2FzLnZlY3Rvcihyb3duYW1lcyhwMmdfbWF0X3N1YikpLCBjb2xuYW1lcyhwMmdfbWF0X3N1YildCiAgCiAgIyBlbGVtZW50d2lzZSBtYXRyaXggbXVsdGlwbGljYXRpb24KICB3ZWlnaHRlZF9wMmdfbWF0IDwtIHAyZ19tYXRfc3ViICogcDJnX2R3CiAgCiAgcHJpbnQocGFzdGUobGVuZ3RoKHdoaWNoKHJvd1N1bXMod2VpZ2h0ZWRfcDJnX21hdCkgPT0gMCkpLCAiZ2VuZXMgaGF2ZSBvbmx5IHplcm8gY29ycmVsYXRpb24gdmFsdWVzLCBzbyB3ZSB3aWxsIHJlbW92ZSB0aGVtLiIpKQogIHdlaWdodGVkX3AyZ19tYXQgPC0gd2VpZ2h0ZWRfcDJnX21hdFtyb3dTdW1zKHdlaWdodGVkX3AyZ19tYXQpICE9IDAsIF0KICBwcmludChwYXN0ZTAoIldlIGFyZSBsZWZ0IHdpdGggIiwgZGltKHdlaWdodGVkX3AyZ19tYXQpWzFdLCAiIGdlbmVzIikpCiAgCiAgIyBjb21wdXRlIGdlbmUgYWN0aXZpdHkgc2NvcmVzIGJhc2VkIG9uIGRpc3RhbmNlLXdlaWdodGVkIHBlYWsyZ2VuZSBtYXRyaXgKICB3ZWlnaHRlZF9zY29yZXMgPC0gZ2VuZV9hY3Rpdml0eV9zY29yZXMocGVha19tYXRfc3ViLCB3ZWlnaHRlZF9wMmdfbWF0KQogIAogIHJldHVybih3ZWlnaHRlZF9zY29yZXMpIAp9CmBgYAoKYGBgI3tyfQp3ZWlnaHRlZF9zY29yZXMgPC0gZGlzdGFuY193ZWlnaHRlZF9nZW5lX2FjdGl2aXR5X3Njb3JlcyhwMmdfbWF0X3N1YiwgZ2VuZU1vZGVsID0gImV4cCgtZGlzdGFuY2UvNTAwMCkiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB3ZWlnaHQgPSA1MDAwMCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwZWFrX21hdCwgbGlua3MsIHAyZywgZ2VuZV9leHByKQpgYGAKIyMgS05OIGFnZ3JlZ2F0ZXMKCgpgYGB7cn0Kcm0obWV0YV9ybmEpCiNybShwMmdfbWF0KQpnYyhyZXNldCA9IFRSVUUpCgptb2RlbF9saXN0IDwtIGMoImV4cCgtYWJzKGRpc3RhbmNlKS81MDAwKSIsICJleHAoLWFicyhkaXN0YW5jZSkvNTAwMDApIiwKICAgICAgICAgICAgICAgICJleHAoLWFicyhkaXN0YW5jZSkvNTAwMDAwKSIsICJleHAoLWFicyhkaXN0YW5jZSkvNTAwMDAwMCkiKQoKIyByZWFkIGluIGtubgpybmFfa25uIDwtIHJlYWRSRFMoIjExX2FkZGVkX1JpY2FyZHNfcGVha3NfcDJnX2VudGlyZV9jaHJvbW9zb21lL1BlYWsyR2VuZUxpbmtzL3NlUk5BLUdyb3VwLUtOTi5yZHMiKQpjZWxsX2FnZ19saXN0IDwtIG1ldGFkYXRhKHJuYV9rbm4pW1sxXV0KCgoKIyBhZ2dyZWdhdGUgZm9yIGdlbmUgZXhwcmVzc2lvbiwgQXJjaFIgZ2VuZSBhY3Rpdml0eSBzY29yZXMgYW5kIHNpbXBsZSBwMmcgbGlua3MKcm5hX2FnZyA8LSBrbm5fYWdncmVnYXRlcyhleHByX21hdF9zdWIsIGNlbGxfYWdnX2xpc3QpCmFyY2hyX2tubiA8LSBhcmNocl9zY29yZXNfbWF0W2FzLnZlY3Rvcihyb3duYW1lcyhybmFfYWdnKSksXQphZ2dfYXJjaHJfa25uIDwtIGtubl9hZ2dyZWdhdGVzKGFyY2hyX2tubiwgY2VsbF9hZ2dfbGlzdCkKYWdnX3AyZ19rbm4gPC0ga25uX2FnZ3JlZ2F0ZXMocDJnX3Njb3JlcywgY2VsbF9hZ2dfbGlzdCkKCiMgY29tcHV0ZSByb3d3aXNlIGNvcnJlbGF0aW9ucwphcmNocl9rbm4gPC0gcm93d2lzZV9jb3JyZWxhdGlvbnMocm5hX2FnZywgYWdnX2FyY2hyX2tubiwgIkFyY2hyIGdlbmUgYWN0aXZpdHkgc2NvcmVzLCBLTk4gYWdncmVnYXRlcyIpCnAyZ19rbm4gPC0gcm93d2lzZV9jb3JyZWxhdGlvbnMocm5hX2FnZywgYWdnX3AyZ19rbm4sICJQZWFrLXRvLWdlbmUgbGlua3MgYWN0aXZpdHkgc2NvcmVzLCBLTk4gYWdncmVnYXRlcyIpCmNvd3Bsb3Q6OnBsb3RfZ3JpZChhcmNocl9rbm5bWzJdXSwgcDJnX2tubltbMl1dLCBuY29sID0gMikKCgojIHByZXBhcmUgbGlzdHMgdG8gc3RvcmUgY29ycmVsYXRpb24gdmVjdG9ycyBhbmQgY29ycmVsYXRpb24gaGlzdG9ncmFtcwpjb3JyX2xpc3QgPC0gbGlzdChhcmNocl9rbm5bWzFdXSwgcDJnX2tubltbMV1dKQoKIyBjb21wdXRlIHRoZSBkaXN0YW5jZS13ZWlnaHRlZCBnZW5lIGFjdGl2aXR5IHNjb3JlcyBmcm9tIHAyZyBsaW5rcyB1c2luZyBkaWZmZXJlbnQgCiMgZGlzdGFuY2Ugd2VpZ2h0IG1vZGVscwpmb3IgKG1vZGVsIGluIG1vZGVsX2xpc3QpewogIHdlaWdodGVkX3Njb3JlcyA8LSBkaXN0YW5jX3dlaWdodGVkX2dlbmVfYWN0aXZpdHlfc2NvcmVzKHAyZ19tYXRfc3ViLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBnZW5lTW9kZWwgPSBtb2RlbCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgd2VpZ2h0ID0gNTAwMDAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGVha19tYXQgPSBwZWFrX21hdCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsaW5rcyA9IGxpbmtzLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwMmdfb3JpZ2luYWwgPSBwMmcsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdlbmVfZXhwciA9IGdlbmVfZXhwcikKICBhZ2dfZGlzdCA8LSBrbm5fYWdncmVnYXRlcyh3ZWlnaHRlZF9zY29yZXMsIGNlbGxfYWdnX2xpc3QpCiAgZGlzdF9rbm4gPC0gcm93d2lzZV9jb3JyZWxhdGlvbnMocm5hX2FnZywgYWdnX2Rpc3QsIG5hbWUgPSBwYXN0ZTAoIlAyZyBhY3Rpdml0eSBzY29yZXMsIGRpc3RhbmNlIHdlaWh0ZWQsIG1vZGVsID0gIiwgbW9kZWwpKQogIHN0b3BpZm5vdChhbnkoaXMubmEoZGlzdF9rbm4pKSA9PSBGQUxTRSkKICAKICBjb3JyX2xpc3QgPC0gYXBwZW5kKGNvcnJfbGlzdCwgZGlzdF9rbm5bWzFdXSkKICBwcmludChkaXN0X2tubltbMl1dKQogICNjb3JyX3Bsb3RzX2xpc3QgPC0gYXBwZW5kKGNvcnJfcGxvdHNfbGlzdCwgZGlzdF9rbm5bWzJdXSkKICAKICBwbG90IDwtIGdncGxvdCgpICsgI2dlb21fZGVuc2l0eV8yZF9maWxsZWQoYWVzKHggPSBjb3JyX2xpc3RbW2ldXSwgCiAgICAgICAgICAgICAgICAgICAgICAjICAgICAgICAgICAgICAgIHkgPSBjb3JyX2xpc3RbWzFdXSksIGFscGhhID0gLjUpICsKICBnZW9tX3BvaW50KGFlcyh4ID0gZGlzdF9rbm5bWzFdXSwgeSA9IGNvcnJfbGlzdFtbMV1dKSkgKwogIGdlb21fbGluZShhZXMoeCA9IGRpc3Rfa25uW1sxXV0sIHkgPSBkaXN0X2tubltbMV1dKSwgY29sID0gInJlZCIpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiTm9uZSIpICArCiAgbGFicyh4ID0gIkNvcnJlbGF0aW9uIGdlbmUgZXhwcmVzc2lvbiAmIHAyZyBhY3Rpdml0eSBzY29yZXMiLAogICAgICAgIHRpdGxlID0gcGFzdGUwKG1vZGVsLCAiS05OIGFnZ3JlZ2F0ZXMiKSwKICAgICAgICB5ID0gIkNvcnJlbGF0aW9uIGdlbmUgZXhwcmVzc2lvbiAmIEFyY2hSIGdlbmUgYWN0aXZpdHkgc2NvcmVzIikKICBwcmludChwbG90KQp9CgpgYGAKCgojIyBTRUFjZWxscyAKCmBgYHtyLCBmaWcud2lkdGg9OCwgZmlnLmhlaWdodD01fQojIHByZXBhcmUgbGlzdHMgdG8gc3RvcmUgY29ycmVsYXRpb24gdmVjdG9ycyBhbmQgY29ycmVsYXRpb24gaGlzdG9ncmFtcwpjb3JyX2xpc3QgPC0gbGlzdChzZWFjZWxsX2NvcnJfYXJjaHJbWzFdXSwgc2VhY2VsbF9jb3JyX3AyZ1tbMV1dKQoKIyBjb21wdXRlIHRoZSBkaXN0YW5jZS13ZWlnaHRlZCBnZW5lIGFjdGl2aXR5IHNjb3JlcyBmcm9tIHAyZyBsaW5rcyB1c2luZyBkaWZmZXJlbnQgCiMgZGlzdGFuY2Ugd2VpZ2h0IG1vZGVscwpmb3IgKG1vZGVsIGluIG1vZGVsX2xpc3QpewogIHdlaWdodGVkX3Njb3JlcyA8LSBkaXN0YW5jX3dlaWdodGVkX2dlbmVfYWN0aXZpdHlfc2NvcmVzKHAyZ19tYXRfc3ViLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBnZW5lTW9kZWwgPSBtb2RlbCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgd2VpZ2h0ID0gNTAwMDAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGVha19tYXQgPSBwZWFrX21hdCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsaW5rcyA9IGxpbmtzLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwMmdfb3JpZ2luYWwgPSBwMmcsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdlbmVfZXhwciA9IGdlbmVfZXhwcikKICBhZ2dfZGlzdCA8LSBjcmVhdGVfc2VhY2VsbF9hZ2dyZWdhdGVzKHdlaWdodGVkX3Njb3Jlcywgc2VhY2VsbHMpCiAgZGlzdF9rbm4gPC0gcm93d2lzZV9jb3JyZWxhdGlvbnMoc2VhY2VsbF9ybmFfYWdnLCBhZ2dfZGlzdCwgbmFtZSA9IHBhc3RlMCgiUDJnIGFjdGl2aXR5IHNjb3JlcywgZGlzdGFuY2Ugd2VpZ2h0ZWQsIG1vZGVsID0gIiwgbW9kZWwpKQogIHN0b3BpZm5vdChhbnkoaXMubmEoZGlzdF9rbm4pKSA9PSBGQUxTRSkKICAKICBjb3JyX2xpc3QgPC0gYXBwZW5kKGNvcnJfbGlzdCwgZGlzdF9rbm5bWzFdXSkKICBwcmludChkaXN0X2tubltbMl1dKQogICNjb3JyX3Bsb3RzX2xpc3QgPC0gYXBwZW5kKGNvcnJfcGxvdHNfbGlzdCwgZGlzdF9rbm5bWzJdXSkKICAKICBwbG90IDwtIGdncGxvdCgpICsgI2dlb21fZGVuc2l0eV8yZF9maWxsZWQoYWVzKHggPSBjb3JyX2xpc3RbW2ldXSwgCiAgICAgICAgICAgICAgICAgICAgICAjICAgICAgICAgICAgICAgIHkgPSBjb3JyX2xpc3RbWzFdXSksIGFscGhhID0gLjUpICsKICBnZW9tX3BvaW50KGFlcyh4ID0gZGlzdF9rbm5bWzFdXSwgeSA9IGNvcnJfbGlzdFtbMV1dKSkgKwogIGdlb21fZGVuc2l0eV8yZF9maWxsZWQoYWVzKHggPSBkaXN0X2tubltbMV1dLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSBjb3JyX2xpc3RbWzFdXSksIGFscGhhID0gMC41KSArCiAgZ2VvbV9saW5lKGFlcyh4ID0gY29ycl9saXN0W1sxXV0sIHkgPSBjb3JyX2xpc3RbWzFdXSksIGNvbCA9ICJyZWQiKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIk5vbmUiKSAgKwogIGxhYnMoeCA9ICJDb3JyZWxhdGlvbiBnZW5lIGV4cHIuICYgcDJnIGFjdGl2aXR5IHNjb3JlcyIsCiAgICAgICAgdGl0bGUgPSBwYXN0ZTAobW9kZWwsICJTRUFDZWxscyIpLAogICAgICAgIHkgPSAiQ29ycmVsYXRpb24gZ2VuZSBleHByLiAmIEFyY2hSIGdlbmUgYWN0aXZpdHkgc2NvcmVzIikKICBwcmludChwbG90KQp9CgpgYGAKCgoKIyBHZW5lIHdpbmRvdywgbm8gZGlzdGFuY2Ugd2VpZ2h0cwoKVGhlcmUgYXJlIHR3byBvcHRpb25zIHdoZW4gZGVmaW5pbmcgdGhlIGdlbmUgd2luZG93LiBPbmUgb3B0aW9uIGlzIHRvIGV4dGVuZCArLy0gCjEwMGJwIHVwLSBhbmQgZG93bnN0cmVhbSBvZiB0aGUgVFNTLiBIb3dldmVyLCBzaW5jZSBnZW5lcyBoYXZlIGRpZmZlcmVudCBzaXplcywKc29tZSBnZW5lIGJvZGllcyBtaWdodCBiZSBtdWNoIGxhcmdlciB0aGFuIHRoZXNlIGdlbmUgd2luZG93cy4gVGhlIHNlY29uZCBvcHRpb24gCmlzIHRvIGV4dGVuZCB0aGUgZ2VuZSB3aW5kb3cgbm90IGZyb20gdGhlIFRTUywgYnV0IGZyb20gdGhlIHN0YXJ0IGFuZCBlbmQgY29ycmRpbmF0ZSAKb2YgdGhlIGdlbmUgYm9keSByZXNwZWN0aXZlbHkuIFRoaXMgd2F5LCBtb3JlIHBlYWtzIHdpbGwgYmUgdGFrZW4gaW50byBjb25zaWRlcmF0aW9uIAppZiBhIGdlbmUgaXMgbGFyZ2VyLCBzaW1wbHkgYmVjYXVzZSB0aGUgZ2VuZSB3aW5kb3cgd2lsbCBiZSBsYXJnZXIuIFRoZXJlZm9yZSwgCmluIEFyY2hSIHRoZXkgdXNlIGFuIGFkZGl0aW9uYWwgd2VpZ2h0IGZvciB0aGUgZ2VuZSBib2R5IHNpemUgdG8gYWNjb3VudCBmb3IgdGhpcwplZmZlY3QuIEhlcmUsIHdlIGV4dGVuZCB0aGUgZ2VuZSB3aW5kb3cgYXJvdW5kIHRoZSBUU1MuIEFzIGNhbiBiZSBzZWVuIGluIAp0aGUgcGxvdCBiZWxvdywgdGhpcyBkb2VzIG5vdCB5aWVsZCBiZXR0ZXIgcmVzdWx0cywgcHJvYmFibHksIGJlY2F1c2Ugd2UgYXJlCnJlbW92aW5nIGEgbG90IG9mIGNvcnJlbGF0aW9ucyB3aGljaCBhcmUgaGlnaCBhbmQsIHRoZXJlZm9yZSwgaW1wb3J0YW50IGZvcgp0aGUgcHJlZGljdGlvbi4gCgpUaGlzIGlzIG5vdCB3aGF0IHdvdWxkIGJlIGV4cGVjdGVkLCBzaW5jZSBzb21lIGhpZ2ggY29ycmVsYXRpb25zIHdpdGhpbgp0aGUgZ2VuZSB3aW5kb3cgYXJlIHZlcnkgbGlrZWx5IHRvIGJlIGJpb2xvZ2ljYWxseSBpbXBvcnRhbnQgYW5kIHNob3VsZApyZWNhcGl0dWxhdGUgZ2VuZSBleHByZXNzaW9uIHF1aXRlIHdlbGwuIFRoaXMgaXMgYWxzbyBzaG93biBieSB0aGUgQXJjaFIgCmdlbmUgYWN0aXZpdHkgc2NvcmVzLCB3aGljaCB1c2UgZ2VuZSB3aW5kb3cgYXMgd2VsbCB0byByZXN0cmljIHRoZSBpbmZsdWVuY2UKb2YgYWNjZXNzaWJsZSByZWdpb25zIHRvIGEgY2VydGFpbiB3aW5kb3cgYXJvdW5kIHRoZSBnZW5lJ3MgVFNTLiBPbmUgcmVhc29uCmNvdWxkIGJlIHRoYXQgdGhlIHBlYWstdG8tZ2VuZSBsaW5rcyBpZGVudGlmaWVkIGJ5IHNpbXBsZSBjb3JyZWxhdGlvbnMgYXJlCm5vdCBiaW9sb2dpY2FsbHkgbWVhbmluZ2Z1bCwgdGhlcmVmb3JlIGFsc28gdmVyeSBmYXIgYXdheSBjb3JyZWxhdGlvbnMgCmFyZSBpbXBvcnRhbnQgZm9yIHJlY2FwaXR1bGF0aW5nIGdlbmUgZXhwcmVzc2lvbi4KCiMjIEdlbmUgd2luZG93IGFyb3VuZCBUU1MKCmBgYHtyLCBmaWcud2lkdGg9MTV9CiMgQXMgaW5wdXQgZm9yIHRoaXMgZnVuY3Rpb24gaXQgaXMgYmVzdCB0byB1c2Ugb25seSB0aGUgbW9zdCBoaWdobHkgdmFyaWFibGUgZ2VuZXMKY29tcHV0ZV9nZW5lX3dpbmRvd19zY29yZSA8LSBmdW5jdGlvbihwMmdfbWF0X3N1YiwgcGVha19tYXQsIGxpbmtzLCBwMmdfb3JpZ2luYWwsIGdlbmVfZXhwcil7CiAgCiAgIyBjcmVhdGUgZ2VuZSBhbm5vdGF0aW9ucyB3aXRoIHN0YXJ0IGNvb3JkaW5hdGUgb2YgZWFjaCBnZW5lCiAgIyBzdWJzZXQgdG8gY29udGFpbiBvbmx5IGdlbmVzIHdoaWNoIGFyZSBpbmNsdWRlZCBpbiBvdXIgcGVhazJnZW5lIG1hdHJpeAogIGdlbmVfYW5ubyA8LSByb3dEYXRhKGdlbmVfZXhwcikgJT4lIAogICAgYXMuZGF0YS5mcmFtZSgpICU+JQogICAgbXV0YXRlKGlkeFJOQSA9IHNlcShucm93KC4pKSkgJT4lIAogICAgZmlsdGVyKG5hbWUgJWluJSByb3duYW1lcyhwMmdfbWF0X3N1YikpICU+JQogICAgbXV0YXRlKHN0cmFuZCA9IGlmZWxzZShzdHJhbmQgPT0gMSwgIisiLCAiLSIpKSAlPiUKICAgIG11dGF0ZShzdGFydF9jb29yZCA9IGlmZWxzZShzdHJhbmQgPT0gIisiLCBzdGFydCwgZW5kKSkgJT4lIAogICAgcmVuYW1lKGdlbmUgPSBuYW1lKSAjJT4lIEdSYW5nZXMoKQoKICAjIGV4dGVuZCBnZW5lIHJlZ2lvbnMgKy8tIDEwMGJwIHVwLSBhbmQgZG93bnN0cmVhbSBvZiB0aGUgVFNTCiAgZ2VuZV9yZWdpb25zICA8LSByZXNpemUoZ2VuZV9hbm5vICU+JSBHUmFuZ2VzKCksIHdpZHRoID0gMSkKICBleHRlbmRlZEdlbmVSZWdpb24gPC0gKHN1cHByZXNzV2FybmluZ3MoZXh0ZW5kR1IoZ2VuZV9yZWdpb25zLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB1cHN0cmVhbSA9IDEwMDAwMCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZG93bnN0cmVhbSA9IDEwMDAwMCkpKQogICMgc3Vic2V0IGF0YWMgZ3JhbmdlcyAmIGdldCBtaWRkbGUgb2YgZWFjaCBwZWFrCiAgcG9zX2F0YWNfZ3JhbmdlcyA8LSAgbWV0YWRhdGEocDJnX29yaWdpbmFsKVtbMV1dICAlPiUgCiAgICBhcy5kYXRhLmZyYW1lKCkgJT4lCiAgICBtdXRhdGUoaWR4QVRBQyA9IHNlcShucm93KC4pKSkgJT4lIAogICAgIyBncm91cF9ieShzZXFuYW1lcykgJT4lCiAgICAjIG11dGF0ZShpZHggPSBzZXFfYWxvbmcoc2VxbmFtZXMpKSAlPiUgCiAgICAjIHVuZ3JvdXAgJT4lCiAgICAjdGlkeXI6OnVuaXRlKGNocl9pZHgsIHNlcW5hbWVzLCBpZHgsIHJlbW92ZSA9IEZBTFNFLCBzZXAgPSAiXyIpICU+JSAKICAgIGZpbHRlcihpZHhBVEFDICVpbiUgY29sbmFtZXMocDJnX21hdF9zdWIpKSAlPiUgCiAgICBtdXRhdGUobWlkZGxlID0gc3RhcnQgKyAzMDApICMlPiUgR1JhbmdlcygpIAogIAogICNUT0RPOiBGaWx0ZXIgZm9yIGdlbmVzIQogIHN0b3BpZm5vdChsZW5ndGgodW5pcXVlKGxpbmtzJGlkeEFUQUMpKSA9PSBkaW0ocG9zX2F0YWNfZ3JhbmdlcylbWzFdXSkKICBzdG9waWZub3QobGVuZ3RoKHVuaXF1ZShsaW5rcyRpZHhSTkEpKSA9PSBkaW0oZ2VuZV9hbm5vKVtbMV1dKQogICNwMmdfZmlsdCA8LSBwMmdfb3JpZ2luYWwgJT4lIGFzLmRhdGEuZnJhbWUoKSAlPiUgZmlsdGVyKGdlbmUgJWluJSByb3duYW1lcyhwMmdfbWF0KSkKICAKICAKICAgICMgZmluZCBvdmVybGFwcGluZyBwZWFrcyBhbmQgZ2VuZSB3aW5kb3cgaW4gY2hyb21vc29tZS1hd2FyZSBmYXNoaW9uCiAgdG1wIDwtIHN1cHByZXNzV2FybmluZ3MoZmluZE92ZXJsYXBzKGV4dGVuZGVkR2VuZVJlZ2lvbiwgcG9zX2F0YWNfZ3JhbmdlcyAlPiUgR1JhbmdlcygpKSkKICAKICBwcmludChwYXN0ZTAoIk91dCBvZiAiLCBzdWJqZWN0TGVuZ3RoKHRtcCksICIgcGVha3Mgb25seSAiLAogICAgICAgICAgICAgICBsZW5ndGgodW5pcXVlKHN1YmplY3RIaXRzKHRtcCkpKSwgIiBwZWFrcyBhcmUgZm91bmQgd2l0aGluIGdlbmUgd2luZG93IG9mIDIwMGtiLiIpKQogIAogIAogICMjIyBzb21lIHBsb3RzCiAgcDEgPC0gKHRtcCAlPiUgYXMuZGF0YS5mcmFtZSgpICU+JSAKICAgICAgICAgZ3JvdXBfYnkocXVlcnlIaXRzKSAlPiUgIyBnZW5lIHJlZ2lvbgogICAgICAgICBzdW1tYXJpemUobiA9IG4oKSkgJT4lICMgZ2V0IG51bWJlciBvZiBwZWFrcyBvdmVybGFwcGluZyB3aXRoIGEgZ2VuZSByZWdpb24KICAgICAgICAgZ2dwbG90KCkgKyBnZW9tX2hpc3RvZ3JhbShhZXMoeCA9IG4pLCBiaW5zID0gMTAwLCBmaWxsPSIjNjliM2EyIikgKwogICAgICAgICBsYWJzKHRpdGxlID0gIm51bWJlciBvZiBwZWFrcyBwZXIgZ2VuZSByZWdpb24gb2Ygc2l6ZSArLy0gMTAwa2IgZnJvbSBUU1MiLAogICAgICAgICAgICAgeCA9ICJudW1iZXIgb2YgcGVha3Mgd2l0aGluIHdpbmRvdyIpKQogIAogIAogIAogICMgY29tYmluZSB0aGUgdGhyZWUgZGF0YWZyYW1lcwogIHAyZ19qb2luIDwtIGxlZnRfam9pbihsaW5rcywgYXMuZGF0YS5mcmFtZShwb3NfYXRhY19ncmFuZ2VzKSwKICAgICAgICAgICAgICAgICAgICAgICAgYnkgPSAiaWR4QVRBQyIpCiAgcDJnX2pvaW4gPC0gbGVmdF9qb2luKHAyZ19qb2luLCBhcy5kYXRhLmZyYW1lKGdlbmVfYW5ubyksCiAgICAgICAgICAgICAgICAgICAgICAgIGJ5ID0gImlkeFJOQSIsIHN1ZmZpeCA9IGMoIi5hdGFjIiwgIi5ybmEiKSkKCiAgIyBjb21wdXRlIGRpc3RhbmNlIGFuZCBkaXN0YW5jZSB3ZWlnaHRzIAogIHAyZ19qb2luIDwtIHAyZ19qb2luICU+JSAKICAgIG11dGF0ZShkaXN0YW5jZSA9IGFicyhzdGFydF9jb29yZCAtIG1pZGRsZSkpIyAlPiUKICAgIyBtdXRhdGUoZGlzdGFuY2Vfd2VpZ2h0ID0gZXZhbChwYXJzZSh0ZXh0PWdlbmVNb2RlbCkpKQogIAogIAogIHAyIDwtIHAyZ19qb2luICU+JSBnZ3Bsb3QoKSArCiAgICBnZW9tX2hpc3RvZ3JhbShhZXMoeCA9IGRpc3RhbmNlKSwgYmlucyA9IDEwMCkgKwogICAgbGFicyh0aXRsZSA9ICJEaXN0YW5jZSIsIHggPSAiZGlzdGFuY2UiKSArCiAgICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgID0gMTAwMDAwLCBjb2xvciA9ICJyZWQiKSAKCiAgCiAgIyBwMiA8LSBwMmdfam9pbiAlPiUgZ2dwbG90KCkgKwogICMgICBnZW9tX2hpc3RvZ3JhbShhZXMoeCA9IChkaXN0YW5jZV93ZWlnaHQpKSwgYmlucyA9IDEwMCkgKwogICMgICBzY2FsZV95X2xvZzEwKCkgKwogICMgICBsYWJzKHRpdGxlID0gIkRpc3RhbmNlIFdlaWdodHMiLCB4ID0gImRpc3RhbmNlIHdlaWdodHMiKSAKCiAgcHJpbnQoY293cGxvdDo6cGxvdF9ncmlkKHAxLCBwMiwgbmNvbCA9IDIpKSMpLCAgbmNvbCA9IDIpKQogIAogIAogIAogIAogICAgCiAgIyBjcmVhdGUgYSBkYXRhZnJhbWUgb2YgYWxsIHBlYWtzIHdoaWNoIG92ZXJsYXAgdGhlaXIgY29ycmVzcG9uZGluZyBnZW5lIHdpbmRvdwogIHBlYWtzX2luX2dlbmVfd2luZG93IDwtIGRhdGEuZnJhbWUoZ2VuZSA9IGdlbmVfcmVnaW9uc1txdWVyeUhpdHModG1wKV0kZ2VuZSwgCiAgICAgICAgICAgICBwZWFrID0gKHBvc19hdGFjX2dyYW5nZXMgJT4lIEdSYW5nZXMoKSlbc3ViamVjdEhpdHModG1wKV0kaWR4QVRBQykgJT4lIAogICAgdW5pdGUocGVha19nZW5lX3dpbmRvdywgZ2VuZSwgcGVhaywgc2VwID0gIiMiLCByZW1vdmUgPSBGQUxTRSkKICAKICAjIGZpbHRlciB0aGUgcDJnIGxpbmsgZGF0YWZyYW1lIGZvciBvbmx5IHBlYWtzIHdoaWNoIGFyZSB3aXRoaW4gYSBnZW5lIHdpbmRvdwogIGNvcnJfd2luZG93IDwtIHAyZ19qb2luICU+JQogICAgdW5pdGUocGVha19nZW5lX3dpbmRvdywgZ2VuZSwgaWR4QVRBQywgc2VwID0gIiMiLCByZW1vdmUgPSBGQUxTRSkgJT4lCiAgICBmaWx0ZXIocGVha19nZW5lX3dpbmRvdyAlaW4lIHBlYWtzX2luX2dlbmVfd2luZG93JHBlYWtfZ2VuZV93aW5kb3cpIAoKCiAgIyMjIFBMT1RTCiAgCiAgcDEgPC0gY29ycl93aW5kb3cgJT4lIAogICAgZ2dwbG90KCkgKwogICAgZ2VvbV9oaXN0b2dyYW0oYWVzKHggPSBDb3JyZWxhdGlvbiksIGJpbnMgPSAyMDAsIGZpbGwgPSAiIzY5YjNhMiIpICsKICAgIGxhYnModGl0bGUgPSAiQ29ycmVsYXRpb24gdmFsdWVzIG9mIHBlYWtzIGZvdW5kIHdpdGhpbiBnZW5lIHdpbmRvd3MiKQogIAogIHAyIDwtIGNvcnJfd2luZG93ICU+JSAKICAgIGdncGxvdCgpICsKICAgIGdlb21faGlzdG9ncmFtKGFlcyh4ID0gZGlzdGFuY2UpLCBiaW5zID0gMjAwLCBmaWxsID0gIiM2OWIzYTIiKSArCiAgICBsYWJzKHRpdGxlID0gIkRpc3RhbmNlIGJldHdlZW4gcGVha3MgYW5kIGdlbmVzIGZvdW5kIHdpdGhpbiBnZW5lIHdpbmRvd3MgYW5kIFRTUyIpCiAgCiAgcDMgPC0gY29ycl93aW5kb3cgJT4lIAogICAgbXV0YXRlKGJpbiA9IGN1dF93aWR0aChkaXN0YW5jZSwgd2lkdGg9MTAwMDAsIGJvdW5kYXJ5PTApKSAlPiUgCiAgICBnZ3Bsb3QoKSArCiAgICBnZW9tX2JveHBsb3QoYWVzKHggPSBiaW4sIHkgPSBDb3JyZWxhdGlvbiksIGZpbGwgPSAiIzY5YjNhMiIpICsKICAgIGxhYnModGl0bGUgPSAiRGlzdGFuY2UgYW5kIENvcnJlbGF0aW9uIHdpdGhpbiBnZW5lIHdpbmRvdywgMTAwMGJwIGJpbnMiLAogICAgICAgICB4ID0gIkRpc3RhbmNlICgxMDAwYnAgYmlucykiKSArCiAgc2NhbGVfeF9kaXNjcmV0ZShndWlkZSA9IGd1aWRlX2F4aXMoYW5nbGUgPSA5MCkpIAoKICBwcmludChjb3dwbG90OjpwbG90X2dyaWQocDEsIHAyLCBwMywgbmNvbCA9IDEpKQogIAogIAogIHAxIDwtIGdncGxvdCgpICsgCiAgICBnZW9tX2hpc3RvZ3JhbShhZXMoeCA9IHJvd1N1bXMocDJnX21hdF9zdWIgPiAwKSksIGJpbnMgPSAyMDAsIGZpbGwgPSAiIzY5YjNhMiIpICsKICAgIHNjYWxlX3lfbG9nMTAoKSArCiAgICBsYWJzKHRpdGxlID0gIiMgcGVha3MgY29ycmVsYXRlZCB3aXRoIGVhY2ggZ2VuZSIsIAogICAgICAgICB4ID0gIm51bWJlciBvZiBwZWFrcyIsIHkgPSAibG9nMTAoY291bnQpIikgCiAgICAKICAKICBwMiA8LSBnZ3Bsb3QoKSArIAogICAgZ2VvbV9oaXN0b2dyYW0oYWVzKHggPSBjb2xTdW1zKHAyZ19tYXRfc3ViID4gMCkpLCBiaW5zID0gNzAsIGZpbGwgPSAiIzY5YjNhMiIpICsKICAgIHNjYWxlX3lfbG9nMTAoKSArCiAgICBsYWJzKHRpdGxlID0gIiMgZ2VuZXMgY29ycmVsYXRlZCB3aXRoIGVhY2ggcGVhayIsCiAgICAgICAgIHkgPSAibG9nMTAoY291bnQpIiwgeCA9ICJudW1iZXIgb2YgZ2VuZXMiKQogIAogIHAzIDwtIGdncGxvdCgpICsgCiAgICBnZW9tX2hpc3RvZ3JhbShhZXMoeCA9IHJvd1N1bXMocDJnX21hdF9zdWIgPiAwKSksIGJpbnMgPSAyMDAsIGZpbGwgPSAiIzY5YjNhMiIpICsKICAgIGxhYnModGl0bGUgPSAiIyBwZWFrcyBjb3JyZWxhdGVkIHdpdGggZWFjaCBnZW5lIiwgCiAgICAgICAgIHggPSAibnVtYmVyIG9mIHBlYWtzIiwgeSA9ICJjb3VudCIpIAogICAgCiAgCiAgcDQgPC0gZ2dwbG90KCkgKyAKICAgIGdlb21faGlzdG9ncmFtKGFlcyh4ID0gY29sU3VtcyhwMmdfbWF0X3N1YiA+IDApKSwgYmlucyA9IDcwLCBmaWxsID0gIiM2OWIzYTIiKSArCiAgICBsYWJzKHRpdGxlID0gIiMgZ2VuZXMgY29ycmVsYXRlZCB3aXRoIGVhY2ggcGVhayIsCiAgICAgICAgIHkgPSAiY291bnQiLCB4ID0gIm51bWJlciBvZiBnZW5lcyIpCiAgCiAgcHJpbnQoY293cGxvdDo6cGxvdF9ncmlkKHAxLCBwMiwgcDMsIHA0LCBuY29sID0gMikpCgogIAoKICAKICAjIAogICMgCiAgIyBwZWFrX21pZGRsZV9yZWdpb24gPC0gcG9zX2F0YWNfZ3JhbmdlcyAlPiUgR1JhbmdlcygpCiAgIyAjIGFkZCB0aGUgaGFsZiB3aWR0aCB0byB0aGUgc3RhcnQgb2YgZWFjaCBwZWFrCiAgIyBzdGFydChwZWFrX21pZGRsZV9yZWdpb24pID0gc3RhcnQocGVha19taWRkbGVfcmVnaW9uKSArIAogICMgICBmbG9vcih3aWR0aChwZWFrX21pZGRsZV9yZWdpb24pIC8gMikKICAjICMgcmVzaXplIHRoZSByYW5nZXMgc28gd2Ugb25seSBoYXZlIHRoZSBtaWRkbGUgb2YgZWFjaCBwZWFrCiAgIyBwZWFrX21pZGRsZV9yZWdpb24gPC0gcmVzaXplKHBlYWtfbWlkZGxlX3JlZ2lvbiwgMSwgInN0YXJ0IikKICAjIAogICMgIyBjb21wdXRlIHRoZSBkaXN0YW5jZXMgYmV0d2VlbiBwZWFrIG1pZGRsZSBhbmQgZ2VuZSBUU1Mgb2YgYWxsIHBlYWtzIHdoaWNoIAogICMgIyBvdmVybGFwIHdpdGggYSBnZW5lIHdpbmRvdwogICMgZGlzdGFuY2UgPC0gZGlzdGFuY2UocmFuZ2VzKGdlbmVfcmVnaW9ucylbcXVlcnlIaXRzKHRtcCldLCAKICAjICAgICAgICAgICAgICAgcmFuZ2VzKHJlc2l6ZShwZWFrX21pZGRsZV9yZWdpb24sIHdpZHRoID0gMSkpW3N1YmplY3RIaXRzKHRtcCldKQogICMgCiAgIyAKICAjICMjIyBQTE9UCiAgIyAjIHAxIDwtIGdncGxvdCgpICsgZ2VvbV9oaXN0b2dyYW0oYWVzKHggPSBkaXN0YW5jZSksIGJpbnMgPSAyMDApICsKICAjICMgICBzY2FsZV95X2xvZzEwKCkgKwogICMgIyAgIGxhYnModGl0bGUgPSAiRGlzdGFuY2UgYmV0d2VlbiBwZWFrIG1pZGRsZSBhbmQgZ2VuZSBUU1Mgd2l0aGluIGEgZ2VuZSB3aW5kb3ciLAogICMgIyAgICAgICAgeSA9ICJsb2cxMChjb3VudCkiKSArCiAgIyAjICAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gMTAwMDAwLCBjb2xvciA9ICJyZWQiKQogICMgCiAgIyAKICAjIAogICMgaXNNaW51cyA8LSBCaW9jR2VuZXJpY3M6OndoaWNoKHN0cmFuZChnZW5lX3JlZ2lvbnMpID09ICItIikKICAjICMgc3VidHJhY3QgdGhlIGdlbmUgc3RhcnQgY29vcmRpbmF0ZSBmcm9tIHRoZSB0aWxlIHN0YXJ0IGNvb3JkaW5hdGUgLT4gcmVsYXRpdmUgZGlzdGFuY2VzCiAgIyBzaWduRGlzdCA8LSBzaWduKHN0YXJ0KHBlYWtfbWlkZGxlX3JlZ2lvbilbc3ViamVjdEhpdHModG1wKV0gLSAKICAjICAgICAgICAgICAgICAgICAgICBzdGFydChyZXNpemUoZ2VuZV9yZWdpb25zLDEsInN0YXJ0IikpW3F1ZXJ5SGl0cyh0bXApXSkKICAjICMgY29udmVydCB0aGUgZGlyZWN0aW9uIG9mIGRpc3RhbmNlIGZvciBhbGwgZGlzdGFuY2VzIGNvcnJlc3BvbmRpbmcgdG8gdGhlIG5lZ2F0aXZlIHN0cmFuZAogICMgc2lnbkRpc3RbaXNNaW51c10gPC0gc2lnbkRpc3RbaXNNaW51c10gKiAtMQogICMgCiAgIyAKICAjIGRpc3RhbmNlIDwtIGRpc3RhbmNlICogc2lnbkRpc3QKICAjIAogICMgCiAgIyAKICAjICMjIyMgUExPVAogICMgcDIgPC0gZ2dwbG90KCkgKyBnZW9tX2hpc3RvZ3JhbShhZXMoeCA9IGRpc3RhbmNlKSwgYmlucyA9IDUwMCkgKyAKICAjICAgc2NhbGVfeV9sb2cxMCgpICsKICAjICAgbGFicyh0aXRsZSA9ICJSZWxhdGl2ZSBkaXN0YW5jZSBvZiBwZWFrcyB0byBUU1Mgd2l0aGluIGEgZ2VuZSB3aW5kb3ciLAogICMgICAgICAgIHggPSAiUmVsYXRpdmUgZGlzdGFuY2UgdG8gVFNTIiwgeSA9ICJsb2cxMChjb3VudCkiKSArIAogICMgICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSBjKDEwMDAwMCwgLTEwMDAwMCksIGNvbG9yID0gInJlZCIpCiAgIyAKICAjIHByaW50KHAyKQogICMgI2Nvd3Bsb3Q6OnBsb3RfZ3JpZChwMSwgcDIsIG5jb2wgPSAxKQoKICBwMmdfbGlua3NfZ2VuZV93aW5kb3cgPC0gTWF0cml4OjpzcGFyc2VNYXRyaXgoCiAgICAgIGkgPSBjb3JyX3dpbmRvdyRpZHhSTkEsIAogICAgICBqID0gY29ycl93aW5kb3ckaWR4QVRBQywgCiAgICAgIHggPSBjb3JyX3dpbmRvdyRDb3JyZWxhdGlvbiwgCiAgICAgIGRpbXMgPSBjKG5yb3coZXhwcl9tYXQpLCBucm93KHBlYWtfbWF0KSksCiAgICAgIGRpbW5hbWVzID0gbGlzdChyb3duYW1lcyhleHByX21hdCkscm93bmFtZXMocGVha19tYXQpKQogICAgKQogIAogIHByaW50KHBhc3RlMCgiVGhlIHBlYWstdG8tZ2VuZSBsaW5rcyBtYXRyaXgsIHJlc3RyaWN0ZWQgdG8gYSArLy0gMTAwa2Igd2luZG93IGFyb3VuZCB0aGUgVFNTIGhhcyBkaW1lbnNpb25zICIsIHNwbGl0KGRpbShwMmdfbGlua3NfZ2VuZV93aW5kb3cpLCAxKSkpCiAgCiAgcHJpbnQocGFzdGUwKCJUaGUgbWF4aW11bSB2YWx1ZSBpczogIiwgbWF4KHAyZ19saW5rc19nZW5lX3dpbmRvdyksICIsIHRoZSBtaW51bSB2YWx1ZSBpczogIiwgbWluKHAyZ19saW5rc19nZW5lX3dpbmRvdykgKSkKICAKICAKICAKICBwMmdfbGlua3NfZ2VuZV93aW5kb3cgPC0gcDJnX2xpbmtzX2dlbmVfd2luZG93W3Jvd1N1bXMocDJnX2xpbmtzX2dlbmVfd2luZG93KSAhPSAwLCBdCiAgcDJnX2xpbmtzX2dlbmVfd2luZG93IDwtIHAyZ19saW5rc19nZW5lX3dpbmRvd1ssIGNvbFN1bXMocDJnX2xpbmtzX2dlbmVfd2luZG93KSAhPSAwXQogIAogIHByaW50KHBhc3RlMCgiQWZ0ZXIgcmVtb3ZpbmcgYW55IHJvd3MgYW5kIGNvbHVtc24gd2hpY2ggZG8gbm90IGNvbnRhaW4gYW55IGxpbmtzIHdlIGFyZSBsZWZ0IHdpdGggIiwgbnJvdyhwMmdfbGlua3NfZ2VuZV93aW5kb3cpLCAiIGdlbmVzIGFuZCAiLCBuY29sKHAyZ19saW5rc19nZW5lX3dpbmRvdyksICIgcGVha3MuIikpCiAgIyBDb21wdXRlIGdlbmUgYWN0aXZpdHkgc2NvcmVzCiAgZ2VuZV93aW5kb3dfc2NvcmVzIDwtIGdlbmVfYWN0aXZpdHlfc2NvcmVzKHBlYWtfbWF0X3N1Yltjb2xuYW1lcyhwMmdfbGlua3NfZ2VuZV93aW5kb3cpLCBdLCBwMmdfbGlua3NfZ2VuZV93aW5kb3cpCiAgZGltKGdlbmVfd2luZG93X3Njb3JlcykKCiAgCiAgcmV0dXJuKGdlbmVfd2luZG93X3Njb3JlcykgCn0KYGBgCgoKYGBge3IsIGZpZy5oZWlnaHQ9OCwgZmlnLndpZHRoPTEwfQpnZW5lX3dpbmRvd19zY29yZXMgPC0gY29tcHV0ZV9nZW5lX3dpbmRvd19zY29yZSgKICBwMmdfbWF0X3N1YiA9IHAyZ19tYXRfc3ViLCAKICBwZWFrX21hdCA9IHBlYWtfbWF0LCAKICBsaW5rcyA9IGxpbmtzLCAKICBwMmdfb3JpZ2luYWwgPSBwMmcsIAogIGdlbmVfZXhwciA9IGdlbmVfZXhwcikKYGBgCgojIyMgS05OIGFnZ3JlZ2F0ZXMKCkZpcnN0LCBJIGNvbXBhcmVkIHRoZSBkaXN0YW5jZSB3ZWlndGhlZCBnZW5lIGFjdGl2aXR5IHNjb3JlcyBiYXNlZCBvbiB0aGUgQXJjaFIgS05OIGFnZ3JlZ2F0ZXMuIAoKCmBgYHtyLCBmaWcud2lkdGg9OCwgZmlnLmhlaWdodD01fQp3ZWlnaHRlZF9zY29yZXNfYWdnIDwtIGtubl9hZ2dyZWdhdGVzKGdlbmVfd2luZG93X3Njb3JlcywgY2VsbF9hZ2dfbGlzdCkKd2VpZ2h0ZWRfa25uX2NvcnIgPC0gcm93d2lzZV9jb3JyZWxhdGlvbnMocm5hX2FnZywgd2VpZ2h0ZWRfc2NvcmVzX2FnZywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlAyZyBsaW5rcyB3aXRoaW4gZ2VuZSB3aW5kb3ciKQp3ZWlnaHRlZF9rbm5fY29ycltbMl1dCgpnZ3Bsb3QoKSArCiAgZ2VvbV9kZW5zaXR5XzJkX2ZpbGxlZChhZXMoeCA9IHdlaWdodGVkX2tubl9jb3JyW1sxXV0sIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSBhcmNocl9rbm5bWzFdXVtuYW1lcyh3ZWlnaHRlZF9rbm5fY29ycltbMV1dKV0pLAogICAgICAgICAgICAgICAgICAgICAgICAgYWxwaGEgPSAuNSkgKwogIGdlb21fcG9pbnQoYWVzKHggPSB3ZWlnaHRlZF9rbm5fY29ycltbMV1dLCB5ID0gYXJjaHJfa25uW1sxXV1bbmFtZXMod2VpZ2h0ZWRfa25uX2NvcnJbWzFdXSldKSkgKwogIGdlb21fbGluZShhZXMoeCA9IHdlaWdodGVkX2tubl9jb3JyW1sxXV0sIHkgPSB3ZWlnaHRlZF9rbm5fY29ycltbMV1dKSwgY29sID0gInJlZCIpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiTm9uZSIpICArCiAgbGFicyh4ID0gIkNvcnJlbGF0aW9uIGJldHdlZW4gZ2VuZSBleHByZXNzaW9uIGFuZCBwMmcgYWN0aXZpdHkgc2NvcmVzIiwKICAgICAgICB0aXRsZSA9ICJQZWFrLXRvLWdlbmUgbGlua3MgYXJlIHJlc3RyaWN0ZWQgdG8gYSBnZW5lIHdpbmRvdyBvZiArLy0gMTAwa2IgYXJvdW5kIFRTUyIsCiAgICAgICAgeSA9ICJDb3JyZWxhdGlvbiBiZXR3ZWVuIGdlbmUgZXhwcmVzc2lvbiBhbmQgQXJjaFIgZ2VuZSBhY3Rpdml0eSBzY29yZXMiKQoKCgpgYGAKCgojIyMgU0VBQ2VsbHMKClNlY29uZCwgSSBjb21wYXJlZCB0aGUgZGlzdGFuY2Ugd2VpZ3RocyB1c2luZyB0aGUgU0VBQ2VsbCBhZ2dyZWdhdGVzLCB3aGljaAp5aWVsZHMgYmV0dGVyIHJlc3VsdHMgYXMgY2FuIGJlIHNlZW4gYWJvdmUuIAoKYGBge3IsIGZpZy53aWR0aD04LCBmaWcuaGVpZ2h0PTV9CmdlbmVfd2luZG93X2FnZyA8LSBjcmVhdGVfc2VhY2VsbF9hZ2dyZWdhdGVzKGdlbmVfd2luZG93X3Njb3Jlcywgc2VhY2VsbHMpCmdlbmVfd2luZG93X2NvcnIgPC0gcm93d2lzZV9jb3JyZWxhdGlvbnMoc2VhY2VsbF9ybmFfYWdnLCBnZW5lX3dpbmRvd19hZ2csCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmFtZSA9ICJHZW5lIHdpbmRvdyBhcm91bmQgVFNTIikKCmdlbmVfd2luZG93X2NvcnJbWzJdXQoKCmdncGxvdCgpICsKICBnZW9tX3BvaW50KGFlcyh4ID0gZ2VuZV93aW5kb3dfY29ycltbMV1dLAogICAgICAgICAgICAgICAgIHkgPSBzZWFjZWxsX2NvcnJfYXJjaHJbWzFdXVtuYW1lcyhnZW5lX3dpbmRvd19jb3JyW1sxXV0pXSkpICsKICBnZW9tX2RlbnNpdHlfMmRfZmlsbGVkKGFlcygKICAgIHkgPSBzZWFjZWxsX2NvcnJfYXJjaHJbWzFdXVtuYW1lcyhnZW5lX3dpbmRvd19jb3JyW1sxXV0pXSwKICAgIHggPSBnZW5lX3dpbmRvd19jb3JyW1sxXV0pLCBhbHBoYSA9IDAuNSkgKwogIGdlb21fbGluZShhZXMoeCA9IHNlYWNlbGxfY29ycl9hcmNocltbMV1dW25hbWVzKGdlbmVfd2luZG93X2NvcnJbWzFdXSldLCAKICAgICAgICAgICAgICAgIHkgPSBzZWFjZWxsX2NvcnJfYXJjaHJbWzFdXVtuYW1lcyhnZW5lX3dpbmRvd19jb3JyW1sxXV0pXSksIAogICAgICAgICAgICBjb2xvciA9ICJyZWQiKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIk5vbmUiKSAgKwogIGxhYnMoeCA9ICJDb3JyZWxhdGlvbiBiZXR3ZWVuIGdlbmUgZXhwcmVzc2lvbiBhbmQgcDJnIGFjdGl2aXR5IHNjb3JlcywgZ2VuZSB3aW5kb3ciLAogICAgICAgIHkgPSAiQ29ycmVsYXRpb24gYmV0d2VlbiBnZW5lIGV4cHJlc3Npb24gYW5kIEFyY2hSIGdlbmUgYWN0aXZpdHkgc2NvcmVzIiwKICAgICAgIHRpdGxlID0gIlBlYWstdG8tZ2VuZSBsaW5rcyB3aXRoaW4gZ2VuZSB3aW5kb3ciKQoKYGBgCgojIEVmZmVjdCBvZiB1c2luZyBkaWZmZXJlbnQgZGlzdGFuY2UgZGVjYXkgcmF0ZXMgCgpIb3cgZG9lcyB0aGUgZGlzdGFuY2Ugd2VpZ2h0IGRpc3RyaWJ1dGlvbiBjaGFuZ2Ugd2l0aCBkaWZmZXJlbnQgZGVjYXkgcmF0ZXM/CgpIZXJlLCB3ZSB1c2UgdGhlIGZvcm11bGEgJGVee1xmcmFjey1hYnMoZGlzdGFuY2UpfXtjfX0kIHdpdGggZGlmZmVyZW4gZGVjYXkgcmF0ZXMKJGMgXGluIFx7NTAwMCwgNTAwMDAsIDUwMDAwMCwgNTAwMDAwMFx9JC4gQWRkaXRpb25hbGx5LCB3ZSB1c2Ugb25seSBwZWFrcyB3aGljaCAKb3ZlcmxhcCB3aXRoIGEgKy8tIDEwMGtiIHdpbmRvdyBmcm9tIHRoZSBUU1MuCgpgYGB7ciwgZmlnLndpZHRoPTEwLGZpZy5oZWlnaHQ9NX0KbW9kZWxfbGlzdCA8LSBjKCJleHAoLWFicyhkaXN0YW5jZSkvNTAwMCkiLCAiZXhwKC1hYnMoZGlzdGFuY2UpLzUwMDAwKSIsCiAgICAgICAgICAgICAgICAiZXhwKC1hYnMoZGlzdGFuY2UpLzUwMDAwMCkiLCAiZXhwKC1hYnMoZGlzdGFuY2UpLzUwMDAwMDApIikKCgphdGFjX2dyYW5nZXMgPC0gbWV0YWRhdGEocDJnKVtbMV1dCiNybmFfZ3JhbmdlcyA8LSBtZXRhZGF0YShwMmdfb3JpZ2luYWwpW1syXV0KZ2VuZV9hbm5vIDwtIHJvd0RhdGEoZ2VuZV9leHByKQoKIyBjcmVhdGUgZ2VuZSBhbm5vdGF0aW9ucyB3aXRoIHN0YXJ0IGNvb3JkaW5hdGUgb2YgZWFjaCBnZW5lCiMgc3Vic2V0IHRvIGNvbnRhaW4gb25seSBnZW5lcyB3aGljaCBhcmUgaW5jbHVkZWQgaW4gb3VyIHBlYWsyZ2VuZSBtYXRyaXgKZ2VuZV9hbm5vIDwtIGdlbmVfYW5ubyAlPiUgYXMuZGF0YS5mcmFtZSgpICU+JQogIG11dGF0ZShpZHhSTkEgPSBzZXEobnJvdyguKSkpICU+JSAKICBmaWx0ZXIobmFtZSAlaW4lIHJvd25hbWVzKHAyZ19tYXRfc3ViKSkgJT4lCiAgbXV0YXRlKHN0cmFuZCA9IGlmZWxzZShzdHJhbmQgPT0gMSwgIisiLCAiLSIpKSAlPiUKICBtdXRhdGUoc3RhcnRfY29vcmQgPSBpZmVsc2Uoc3RyYW5kID09ICIrIiwgc3RhcnQsIGVuZCkpICU+JSAKICByZW5hbWUoZ2VuZSA9IG5hbWUpICMlPiUgR1JhbmdlcygpCgojIHN1YnNldCBhdGFjIGdyYW5nZXMgJiBnZXQgbWlkZGxlIG9mIGVhY2ggcGVhawpwb3NfYXRhY19ncmFuZ2VzIDwtIGF0YWNfZ3JhbmdlcyAgJT4lIAogIGFzLmRhdGEuZnJhbWUoKSAlPiUKICBtdXRhdGUoaWR4QVRBQyA9IHNlcShucm93KC4pKSkgJT4lIAogICMgZ3JvdXBfYnkoc2VxbmFtZXMpICU+JQogICMgbXV0YXRlKGlkeCA9IHNlcV9hbG9uZyhzZXFuYW1lcykpICU+JSAKICAjIHVuZ3JvdXAgJT4lCiAgI3RpZHlyOjp1bml0ZShjaHJfaWR4LCBzZXFuYW1lcywgaWR4LCByZW1vdmUgPSBGQUxTRSwgc2VwID0gIl8iKSAlPiUgCiAgZmlsdGVyKGlkeEFUQUMgJWluJSBjb2xuYW1lcyhwMmdfbWF0X3N1YikpICU+JSAKICBtdXRhdGUobWlkZGxlID0gc3RhcnQgKyAzMDApICMlPiUgR1JhbmdlcygpIAoKCgojIGNvbWJpbmUgdGhlIHRocmVlIGRhdGFmcmFtZXMKcDJnX2pvaW4gPC0gbGVmdF9qb2luKGxpbmtzLCBhcy5kYXRhLmZyYW1lKHBvc19hdGFjX2dyYW5nZXMpLAogICAgICAgICAgICAgICAgICAgICAgYnkgPSAiaWR4QVRBQyIpCnAyZ19qb2luIDwtIGxlZnRfam9pbihwMmdfam9pbiwgYXMuZGF0YS5mcmFtZShnZW5lX2Fubm8pLAogICAgICAgICAgICAgICAgICAgICAgYnkgPSAiaWR4Uk5BIiwgc3VmZml4ID0gYygiLmF0YWMiLCAiLnJuYSIpKQoKIyBjb21wdXRlIGRpc3RhbmNlIGFuZCBkaXN0YW5jZSB3ZWlnaHRzIApwMmdfam9pbiA8LSBwMmdfam9pbiAlPiUgCiAgbXV0YXRlKGRpc3RhbmNlID0gYWJzKHN0YXJ0X2Nvb3JkIC0gbWlkZGxlKSkgJT4lCiAgbXV0YXRlKHJlbF9kaXN0YW5jZSA9IHN0YXJ0X2Nvb3JkIC0gbWlkZGxlKQogIyBtdXRhdGUoZGlzdGFuY2Vfd2VpZ2h0ID0gZXZhbChwYXJzZSh0ZXh0PWdlbmVNb2RlbCkpKQoKZm9yIChtb2RlbCBpbiBtb2RlbF9saXN0KXsgCiMgY29tcHV0ZSBkaXN0YW5jZSBhbmQgZGlzdGFuY2Ugd2VpZ2h0cyAKICBwMmdfam9pbiA8LSBwMmdfam9pbiAlPiUgCiAgICBtdXRhdGUoZGlzdGFuY2UgPSBhYnMoc3RhcnRfY29vcmQgLSBtaWRkbGUpKSAlPiUKICAgIG11dGF0ZShkaXN0YW5jZV93ZWlnaHQgPSBldmFsKHBhcnNlKHRleHQ9bW9kZWwpKSkKICAKICBwMSA8LSBwMmdfam9pbiAlPiUgZ2dwbG90KCkgKwogICAgZ2VvbV9oaXN0b2dyYW0oYWVzKHggPSBkaXN0YW5jZSksIGJpbnMgPSAyMDAsIGZpbGw9IiM2OWIzYTIiKSArCiAgICBsYWJzKHRpdGxlID0gIkRpc3RhbmNlIGJldHdlZW4gcGVha3MgYW5kIGdlbmVzIiwgeCA9ICJkaXN0YW5jZSIpICsKICAgIGdlb21fdmxpbmUoeGludGVyY2VwdCAgPSA1MDAwLCBjb2xvciA9ICJyZWQiKSArCiAgICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgID0gMjUwMDAwLCBjb2xvciA9ICJvcmFuZ2UiKQogIAogIHAyIDwtIHAyZ19qb2luICU+JSBnZ3Bsb3QoKSArCiAgICBnZW9tX2hpc3RvZ3JhbShhZXMoeCA9IChkaXN0YW5jZV93ZWlnaHQpKSwgYmlucyA9IDIwMCwgZmlsbD0iIzY5YjNhMiIpICsKICAgIHNjYWxlX3lfbG9nMTAoKSArCiAgICBsYWJzKHRpdGxlID0gcGFzdGUwKCJEaXN0YW5jZV9kZWNheTogIiwgbW9kZWwpLAogICAgICAgICB4ID0gImRpc3RhbmNlIHdlaWdodHMiLCB5ID0gImxvZzEwKGNvdW50cykiKQogIAogIHByaW50KGNvd3Bsb3Q6OnBsb3RfZ3JpZChwMSwgcDIsIG5jb2wgPSAyKSkKCn0KICAjIFJlbGF0aW9uc2hpcCBiZXR3ZWVuIGRpc3RhbmNlIGFuZCBjb3JyZWxhdGlvbiB2YWx1ZQojIHAzIDwtIHAyZ19qb2luICU+JSBnZ3Bsb3QoKSArCiMgICBnZW9tX3BvaW50KGFlcyh4ID0gQ29ycmVsYXRpb24sIHkgPSBkaXN0YW5jZSkpICsKIyAgIGxhYnModGl0bGUgPSAiRGlzdGFuY2UgdnMuIGNvcnJlbGF0aW9uIGJldHdlZW4gcGVha3MgYW5kIGdlbmVzIiwKIyAgICAgICAgeCA9ICJDb3JyZWxhdGlvbiBiZXR3ZWVuIHBlYWsgYW5kIGdlbmUiLCAKIyAgICAgICAgeSA9ICJEaXN0YW5jZSBiZXR3ZWVuIHBlYWsgYW5kIGdlbmUiKQojIAojIAojIHA0IDwtIHAyZ19qb2luICU+JSBnZ3Bsb3QoKSArCiMgICBnZW9tX3BvaW50KGFlcyh4ID0gQ29ycmVsYXRpb24sIHkgPSBkaXN0YW5jZV93ZWlnaHQpKSArCiMgICBsYWJzKHRpdGxlID0gIkRpc3RhbmNlIHZzLiBjb3JyZWxhdGlvbiBiZXR3ZWVuIHBlYWtzIGFuZCBnZW5lcyIsCiMgICAgICAgIHggPSAiQ29ycmVsYXRpb24gYmV0d2VlbiBwZWFrIGFuZCBnZW5lIiwgCiMgICAgICAgIHkgPSAiRGlzdGFuY2Ugd2VpZ2h0cyBiZXR3ZWVuIHBlYWsgYW5kIGdlbmUiKQoKCiNjb3dwbG90OjpwbG90X2dyaWQocDEsIHAyLCBuY29sID0gMSkKCmBgYAoKCiMjIyBSZWxhdGlvbnNoaXAgYmV0d2VlbiBkaXN0YW5jZSBhbmQgY29ycmVsYXRpb24gdmFsdWVzCgpgYGAje3IsZmlnLndpZHRoPTE1fQoKIyBPbG90IHJlbGF0aW9uc2hpcCBiZXR3ZWVuIGRpc3RhbmNlIGFuZCBjb3JyZWxhdGlvbiBhcyBkZW5zaXR5IHBsb3RzCnAxIDwtIHAyZ19qb2luICU+JSBnZ3Bsb3QoKSArIAogIGdlb21fZGVuc2l0eV8yZF9maWxsZWQoYWVzKHggPSBDb3JyZWxhdGlvbiwgeSA9IGRpc3RhbmNlKSkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJOb25lIikgKwogIGxhYnModGl0bGUgPSAiUmVsYXRpb25zaGlwIGJldHdlZW4gZGlzdGFuY2UgYW5kIGNvcnJlbGF0aW9uIikKCnAyIDwtIHAyZ19qb2luICU+JQogIGZpbHRlcihDb3JyZWxhdGlvbiA+IDAuMykgJT4lIAogIGdncGxvdCgpICsgCiAgZ2VvbV9kZW5zaXR5XzJkX2ZpbGxlZChhZXMoeCA9IENvcnJlbGF0aW9uLCB5ID0gZGlzdGFuY2UpKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIk5vbmUiKSArCiAgbGFicyh0aXRsZSA9ICJSZWxhdGlvbnNoaXAgYmV0d2VlbiBkaXN0YW5jZSBhbmQgY29ycmVsYXRpb24iKQoKcDMgPC0gcDJnX2pvaW4gJT4lCiAgZmlsdGVyKENvcnJlbGF0aW9uID4gMC42KSAlPiUgCiAgZ2dwbG90KCkgKyAKICBnZW9tX2RlbnNpdHlfMmRfZmlsbGVkKGFlcyh4ID0gQ29ycmVsYXRpb24sIHkgPSBkaXN0YW5jZSkpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiTm9uZSIpICsKICBsYWJzKHRpdGxlID0gIlJlbGF0aW9uc2hpcCBiZXR3ZWVuIGRpc3RhbmNlIGFuZCBjb3JyZWxhdGlvbiIpCgpjb3dwbG90OjpwbG90X2dyaWQocDEsIHAyLCBwMywgbmNvbCA9IDIpCmBgYAoKCmBgYCN7cn0KcDJnICU+JSAgCiAgbXV0YXRlKGJpbj1jdXRfd2lkdGgoZGlzdGFuY2UsIHdpZHRoPTEwMDAwMCwgYm91bmRhcnk9MCkpICU+JQogIGZpbHRlcihkaXN0YW5jZSA8IDEwMDAwMDAwKSAlPiUgCiAgZ2dwbG90KCkgKwogIGdlb21fYm94cGxvdChhZXMoeCA9IGJpbiwgeSA9IENvcnJlbGF0aW9uKSwgZmlsbD0iIzY5YjNhMiIpICsKICAjZ2VvbV92bGluZSh4aW50ZXJjZXB0ICA9IDI1MDAwMCwgY29sb3IgPSAicmVkIikgKwogIGxhYnModGl0bGUgPSAiUmVsYXRpb25zaGlwIGJldHdlZW4gZGlzdGFuY2UgYW5kIGNvcnJlbGF0aW9uIG9mIHAyZyBsaW5rcywgMTAwa2IgYmlucyIsCiAgICAgICB4ID0gIkRpc3RhbmNlIGJldHdlZW4gcGVha3MgYW5kIGdlbmVzIHdpdGhpbiAyNTBrYiIsIHkgPSAiQ29ycmVsYXRpb24iKSArIAogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHZqdXN0ID0gMC41LCBoanVzdD0xKSkKYGBgCgoKYGBge3IsIGZpZy53aWR0aD0xMH0KcDJnX2pvaW4gJT4lICAKICBtdXRhdGUoYmluPWN1dF93aWR0aChkaXN0YW5jZSwgd2lkdGg9MTAwMDAwLCBib3VuZGFyeT0wKSkgJT4lCiAgZmlsdGVyKGRpc3RhbmNlIDwgMTAwMDAwMDApICU+JSAKICBnZ3Bsb3QoKSArCiAgZ2VvbV9ib3hwbG90KGFlcyh4ID0gYmluLCB5ID0gQ29ycmVsYXRpb24pLCBmaWxsPSIjNjliM2EyIikgKwogICNnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgID0gMjUwMDAwLCBjb2xvciA9ICJyZWQiKSArCiAgbGFicyh0aXRsZSA9ICJSZWxhdGlvbnNoaXAgYmV0d2VlbiBkaXN0YW5jZSBhbmQgY29ycmVsYXRpb24gb2YgcDJnIGxpbmtzLCAxMDBrYiBiaW5zIiwKICAgICAgIHggPSAiRGlzdGFuY2UgYmV0d2VlbiBwZWFrcyBhbmQgZ2VuZXMgd2l0aGluIDI1MGtiIiwgeSA9ICJDb3JyZWxhdGlvbiIpICsgCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgdmp1c3QgPSAwLjUsIGhqdXN0PTEpKQpgYGAKCmBgYHtyLCBmaWcud2lkdGg9MjIsIGZpZy5oZWlnaHQ9MTV9CgpwMSA8LSBwMmdfam9pbiAlPiUgIAogIG11dGF0ZShiaW49Y3V0X3dpZHRoKGRpc3RhbmNlLCB3aWR0aD0xMDAwMDAsIGJvdW5kYXJ5PTApKSAlPiUKICBmaWx0ZXIoZGlzdGFuY2UgPCAxMDAwMDAwMCAmIENvcnJlbGF0aW9uID4gMC41KSAlPiUgCiAgZ2dwbG90KCkgKwogIGdlb21fYm94cGxvdChhZXMoeCA9IGJpbiwgeSA9IENvcnJlbGF0aW9uKSwgZmlsbD0iIzY5YjNhMiIpICsKICBsYWJzKHRpdGxlID0gIlJlbGF0aW9uc2hpcCBiZXR3ZWVuIGRpc3RhbmNlIGFuZCBjb3JyZWxhdGlvbiBvZiBwMmcgbGlua3MsIDEwMGtiIGJpbnMiLAogICAgICAgeCA9ICJEaXN0YW5jZSA8IDFlXjcgYnAiLCB5ID0gIkNvcnJlbGF0aW9uID4gMC41IikgKyAKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCB2anVzdCA9IDAuNSwgaGp1c3Q9MSkpCgpwMiA8LSBwMmdfam9pbiAlPiUgIAogIG11dGF0ZShiaW49Y3V0X3dpZHRoKGRpc3RhbmNlLCB3aWR0aD0xMDAwMDAsIGJvdW5kYXJ5PTApKSAlPiUKICBmaWx0ZXIoZGlzdGFuY2UgPCAxMDAwMDAwMCAmIENvcnJlbGF0aW9uID4gMC44KSAlPiUgCiAgZ2dwbG90KCkgKwogIGdlb21fYm94cGxvdChhZXMoeCA9IGJpbiwgeSA9IENvcnJlbGF0aW9uKSwgZmlsbD0iIzY5YjNhMiIpICsKICBsYWJzKHRpdGxlID0gIlJlbGF0aW9uc2hpcCBiZXR3ZWVuIGRpc3RhbmNlIGFuZCBjb3JyZWxhdGlvbiBvZiBwMmcgbGlua3MsIDEwMGtiIGJpbnMiLAogICAgICAgeCA9ICJEaXN0YW5jZSA8IDFlXjcgYnAiLCB5ID0gIkNvcnJlbGF0aW9uID4gMC44IikgKyAKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCB2anVzdCA9IDAuNSwgaGp1c3Q9MSkpCgpwMyA8LSBwMmdfam9pbiAlPiUgIAogIG11dGF0ZShiaW49Y3V0X3dpZHRoKGRpc3RhbmNlLCB3aWR0aD0xMDAwMDAsIGJvdW5kYXJ5PTApKSAlPiUKICBmaWx0ZXIoZGlzdGFuY2UgPCAxMDAwMDAwMCAmIENvcnJlbGF0aW9uIDwgMC41KSAlPiUgCiAgZ2dwbG90KCkgKwogIGdlb21fYm94cGxvdChhZXMoeCA9IGJpbiwgeSA9IENvcnJlbGF0aW9uKSwgZmlsbD0iIzY5YjNhMiIpICsKICBsYWJzKHRpdGxlID0gIlJlbGF0aW9uc2hpcCBiZXR3ZWVuIGRpc3RhbmNlIGFuZCBjb3JyZWxhdGlvbiBvZiBwMmcgbGlua3MsIDEwMGtiIGJpbnMiLAogICAgICAgeCA9ICJEaXN0YW5jZSA8IDFlXjcgYnAiLCB5ID0gIkNvcnJlbGF0aW9uIDwgMC41IikgKyB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCB2anVzdCA9IDAuNSwgaGp1c3Q9MSkpCgoKCnA0IDwtIHAyZ19qb2luICU+JSAgCiAgbXV0YXRlKGJpbj1jdXRfd2lkdGgoZGlzdGFuY2UsIHdpZHRoPTEwMDAsIGJvdW5kYXJ5PTApKSAlPiUKICBmaWx0ZXIoZGlzdGFuY2UgPCAxMDAwMDAgJiBDb3JyZWxhdGlvbiA+IDAuNSkgJT4lIAogIGdncGxvdCgpICsKICBnZW9tX2JveHBsb3QoYWVzKHggPSBiaW4sIHkgPSBDb3JyZWxhdGlvbiksIGZpbGw9IiM2OWIzYTIiKSArCiAgbGFicyh0aXRsZSA9ICJSZWxhdGlvbnNoaXAgYmV0d2VlbiBkaXN0YW5jZSBhbmQgY29ycmVsYXRpb24gb2YgcDJnIGxpbmtzLCAxa2IgYmlucyIsCiAgICAgICB4ID0gIkRpc3RhbmNlIDwgMTAwa2IiLCB5ID0gIkNvcnJlbGF0aW9uID4gMC41IikgKyB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCB2anVzdCA9IDAuNSwgaGp1c3Q9MSkpCgoKY293cGxvdDo6cGxvdF9ncmlkKHAxLCBwMiwgcDMsIHA0LCBuY29sID0gMikKYGBgCgpMZXRzIGhhdmUgYSBsb29rIGF0IGNvcnJlbGF0aW9uIHZhbHVlcyBiZXR3ZWVuIHBlYWtzIHdpdGhpbiB0aGUgcHJvbW90ZXIgcmVnaW9uCm9mIGEgVFNTLCBuYW1lbHkgNWtiIHVwc3RyZWFtIG9mIHRoZSBUU1MuCgpgYGB7ciwgZmlnLndpZHRoPTh9CnAyZ19qb2luICU+JSAgCiAgbXV0YXRlKGJpbj1jdXRfd2lkdGgocmVsX2Rpc3RhbmNlLCB3aWR0aD0xMDAsIGJvdW5kYXJ5PTApKSAlPiUKICBmaWx0ZXIocmVsX2Rpc3RhbmNlIDwgMCAmIHJlbF9kaXN0YW5jZSA+PSAtNTAwMCkgJT4lIAogIGdncGxvdCgpICsKICBnZW9tX2JveHBsb3QoYWVzKHggPSBiaW4sIHkgPSBDb3JyZWxhdGlvbiksIGZpbGw9IiM2OWIzYTIiKSArCiAgI2dlb21fdmxpbmUoeGludGVyY2VwdCAgPSAyNTAwMDAsIGNvbG9yID0gInJlZCIpICsKICBsYWJzKHRpdGxlID0gIkRpc3RhbmNlIC01a2IgdXBzdHJlYW0gb2YgVFNTLCAxMDBicCBiaW5zIiwKICAgICAgIHggPSAiRGlzdGFuY2UgLTVrYiB1cHN0cmVhbSBvZiBUU1MiLCB5ID0gIkNvcnJlbGF0aW9uIikgKyAKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCB2anVzdCA9IDAuNSwgaGp1c3Q9MSkpCmBgYAoKCiMjIyBUcnkgZGlzdGFuY2UgZGVjYXkgcmF0ZXMKCgpgYGB7cn0Kcm5hX2tubiA8LSByZWFkUkRTKCIxMV9hZGRlZF9SaWNhcmRzX3BlYWtzX3AyZ19lbnRpcmVfY2hyb21vc29tZS9QZWFrMkdlbmVMaW5rcy9zZVJOQS1Hcm91cC1LTk4ucmRzIikKI3JuYV9hZ2dfbWF0IDwtIGFzc2F5cyhybmFfa25uKVtbMV1dCiNyb3duYW1lcyhybmFfYWdnX21hdCkgPC0gcm93RGF0YShybmFfa25uKSRuYW1lCgpjZWxsX2FnZ19saXN0IDwtIG1ldGFkYXRhKHJuYV9rbm4pW1sxXV0KCgprbm5fYWdncmVnYXRlcyA8LSBmdW5jdGlvbihtYXRyaXgsIGNlbGxfYWdnX2xpc3QpewogICMgZW1wdHkgbWF0cml4IHRvIHN0b3JlIGFnZ3JlZ2F0ZXMKICBhZ2cgPC0gbWF0cml4KGRhdGEgPSAwLAogICAgICAgICAgICAgICAgbnJvdyA9IGRpbShtYXRyaXgpWzFdLAogICAgICAgICAgICAgICAgbmNvbCA9IGxlbmd0aChjZWxsX2FnZ19saXN0KSwKICAgICAgICAgICAgICAgIGRpbW5hbWVzID0gbGlzdChyb3duYW1lcyhtYXRyaXgpLCBOVUxMKSkKICAKICBmb3IgKGkgaW4gc2VxLmludChsZW5ndGgoY2VsbF9hZ2dfbGlzdCkpKSB7CiAgICBhZ2dbLCBpXSA8LSByb3dTdW1zKG1hdHJpeFssIGNlbGxfYWdnX2xpc3RbW2ldXV0pCiAgfQogIHJldHVybihhZ2cpCn0KCgpybmFfYWdnIDwtIGtubl9hZ2dyZWdhdGVzKGV4cHJfbWF0X3N1YiwgY2VsbF9hZ2dfbGlzdCkKYXJjaHJfa25uIDwtIGFyY2hyX3Njb3Jlc19tYXRbYXMudmVjdG9yKHJvd25hbWVzKGFnZ19wMmdfa25uKSksXQphZ2dfYXJjaHJfa25uIDwtIGtubl9hZ2dyZWdhdGVzKGFyY2hyX2tubiwgY2VsbF9hZ2dfbGlzdCkKCmFnZ19wMmdfa25uIDwtIGtubl9hZ2dyZWdhdGVzKHAyZ19zY29yZXMsIGNlbGxfYWdnX2xpc3QpCmFnZ19kaXN0IDwtIGtubl9hZ2dyZWdhdGVzKHdlaWdodGVkX3Njb3JlcywgY2VsbF9hZ2dfbGlzdCkKYGBgCgoKYGBge3IsIGZpZy53aWR0aD0xMH0KYXJjaHJfa25uIDwtIHJvd3dpc2VfY29ycmVsYXRpb25zKHJuYV9hZ2csIGFnZ19hcmNocl9rbm4sICJBcmNociBnZW5lIGFjdGl2aXR5IHNjb3JlcyIpCnAyZ19rbm4gPC0gcm93d2lzZV9jb3JyZWxhdGlvbnMocm5hX2FnZywgYWdnX3AyZ19rbm4sICJQZWFrLXRvLWdlbmUgbGlua3MgYWN0aXZpdHkgc2NvcmVzIikKZGlzdF9rbm4gPC0gcm93d2lzZV9jb3JyZWxhdGlvbnMocm5hX2FnZywgYWdnX2Rpc3QsICJQZWFrLXRvX2dlbmUgbGlua3MgYWN0aXZpdHkgc2NvcmVzIHdlaWdodGVkIGJ5IGRpc3RhbmNlIikKY293cGxvdDo6cGxvdF9ncmlkKGFyY2hyX2tubltbMl1dLCBwMmdfa25uW1syXV0sIGRpc3Rfa25uW1syXV0sIG5jb2wgPSAyKQoKcDEgPC0gZ2dwbG90KCkgKyBnZW9tX2RlbnNpdHlfMmRfZmlsbGVkKGFlcyh4ID0gcDJnX2tubltbMV1dLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5ID0gYXJjaHJfa25uW1sxXV0pLCBhbHBoYSA9IC41KSArCiAgZ2VvbV9wb2ludChhZXMoeCA9IHAyZ19rbm5bWzFdXSwgeSA9IGFyY2hyX2tubltbMV1dKSkgKwogIGdlb21fbGluZShhZXMoeCA9IHAyZ19rbm5bWzFdXSwgeSA9IHAyZ19rbm5bWzFdXSksIGNvbCA9ICJyZWQiKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIk5vbmUiKSAKICAKICAKcDIgPC0gZ2dwbG90KCkgKyBnZW9tX2RlbnNpdHlfMmRfZmlsbGVkKGFlcyh4ID0gZGlzdF9rbm5bWzFdXSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeSA9IGFyY2hyX2tubltbMV1dKSwgYWxwaGEgPSAuNSkgKwogIGdlb21fcG9pbnQoYWVzKHggPSBkaXN0X2tubltbMV1dLCB5ID0gYXJjaHJfa25uW1sxXV0pKSArCiAgZ2VvbV9saW5lKGFlcyh4ID0gZGlzdF9rbm5bWzFdXSwgeSA9IGRpc3Rfa25uW1sxXV0pLCBjb2wgPSAicmVkIikgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJOb25lIikgCgpjb3dwbG90OjpwbG90X2dyaWQocDEsIHAyLCBuY29sID0gMikKYGBgCgoKCiMgVEFEIGJvdW5kYXJpZXMKCkluIGNhc2UgSGktQyBkYXRhIGFyZSBhdmFpbGFibGUsIFRBRCBib3VuZGFyaWVzIGNvdWxkIGFpZCBpbiBmaW5kaW5nIApwZWFrLXRvLWdlbmUgbGlua3MuIFNldHRpbmcgYSBkaXN0YW5jZSBkZWNheSByYXRlIHRvIHRoZSBzYW1lIHZhbHVlCmZvciBhbGwgZ2VuZXMgYW5kIGNlbGx0eXBlcywgZG9lcyBub3QgZ2l2ZSBjcmVkaXQgdG8gdGhlIGJpb2xvZ2ljYWwgdmFyaWFiaWxpdHkgYXNzb2NpYXRlZCB3aXRoIGdlbmUgcmVndWxhdGlvbi4gSW4gW0BadWluMjAyMl0gaXQgaGFzIGJlZW4Kc2hvd24gZXhwZXJpbWVudGFsbHksIHRoYXQgaW50ZXJhY3Rpb25zIGJldHdlZW4gcmVndWxhdG9yeSBlbGVtZW50cyAKZGVjYXkgZXhwb25lbnRpYWxseSB3aXRoaW4gVEFEIGJvdW5kYXJpZXMgYW5kIGFsbW9zdCBkaXNhcHBlYXIgY29tcGxldGVseSBiZXlvbmQgVEFEIGJvdW5kYXJpZXMuIEhlcmUsIEkgcmVzdHJpY3RlZCB0aGUgcGVhay10by1nZW5lIGxpbmtzIGlkZW50aWZpZWQgYnkgQXJjaFIgdG8gd2l0aGluIFRBRCBib3VuZGFyaWVzIGFuZCBjb21wdXRlZCBnZW5lIGFjdGl2aXR5IHNjb3JlcyBhZ2Fpbi4KCmBgYHtyfQp0YWRfYm91bmRhcmllcyA8LSBhcy5kYXRhLmZyYW1lKHJlYWQudGFibGUoImp1cHl0ZXJfbm90ZWJvb2tzL3RhZF9lNzUuYmVkIiwgaGVhZGVyID0gRkFMU0UsIHNlcCA9ICJcdCIsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSwgcXVvdGUgPSAiIikpCnRhZF9ib3VuZGFyaWVzIDwtIHRhZF9ib3VuZGFyaWVzICU+JSAKICByZW5hbWUoc2VxbmFtZXMgPSBWMSwgc3RhcnQgPSBWMiwgZW5kID0gVjMpICU+JSAKICBHUmFuZ2VzKCkKCnAxIDwtIGdncGxvdCgpICsgZ2VvbV9oaXN0b2dyYW0oYWVzKHggPSB3aWR0aChnZW5lX2Fubm8gJT4lIEdSYW5nZXMoKSkpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYmlucyA9IDIwMCkgKwogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IG1lZGlhbih3aWR0aChnZW5lX2Fubm8gJT4lIEdSYW5nZXMoKSkpLCAKICAgICAgICAgICAgIGNvbG9yID0gIm9yYW5nZSIpICsKICBsYWJzKHRpdGxlID0gcGFzdGUwKCJEaXN0cmlidXRpb24gb2YgZ2VuZSBzaXplLCBtZWRpYW4gc2l6ZSA9ICIsCiAgICAgICAgICAgICAgICAgICAgICBtZWRpYW4od2lkdGgoZ2VuZV9hbm5vICU+JSBHUmFuZ2VzKCkpKSksCiAgICAgICB4ID0gIkdlbmUgc2l6ZSBpbiBicCIpCgpwMiA8LSBnZ3Bsb3QoKSArIGdlb21faGlzdG9ncmFtKGFlcyh4ID0gd2lkdGgodGFkX2JvdW5kYXJpZXMpKSwgYmlucyA9IDIwMCkgKwogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IG1lZGlhbih3aWR0aCh0YWRfYm91bmRhcmllcykpLCBjb2xvciA9ICJvcmFuZ2UiKSArCiAgbGFicyh0aXRsZSA9IHBhc3RlMCgiRGlzdHJpYnV0aW9uIG9mIFRBRCBib3VuZGFyeSBzaXplLCBtZWRpYW4gc2l6ZSA9ICIsCiAgICAgICBtZWRpYW4od2lkdGgodGFkX2JvdW5kYXJpZXMpKSksCiAgICAgIHggPSAiVEFEIGJvdW5kYXJ5IHNpemUgaW4gYnAiKQoKY293cGxvdDo6cGxvdF9ncmlkKHAxLCBwMiwgbmNvbCA9IDEpCmBgYAoKV2hhdCBpcyB0aGUgZGlzdHJpYnV0aW9uIG9mIHBlYWtzIGFuZCBnZW5lcyB3aXRoaW4gVEFEIGJvdW5kYXJpZXM/CgpgYGB7ciwgZmlnLndpZHRoPTEyLCBmaWcuaGVpZ2h0PTV9CmdlbmVfYW5ubyA8LSByb3dEYXRhKGdlbmVfZXhwcikKCiMgY3JlYXRlIGdlbmUgYW5ub3RhdGlvbnMgd2l0aCBzdGFydCBjb29yZGluYXRlIG9mIGVhY2ggZ2VuZQojIHN1YnNldCB0byBjb250YWluIG9ubHkgZ2VuZXMgd2hpY2ggYXJlIGluY2x1ZGVkIGluIG91ciBwZWFrMmdlbmUgbWF0cml4CmdlbmVfYW5ubyA8LSBnZW5lX2Fubm8gJT4lIGFzLmRhdGEuZnJhbWUoKSAlPiUKICBtdXRhdGUoaWR4Uk5BID0gc2VxKG5yb3coLikpKSAlPiUgCiAgZmlsdGVyKG5hbWUgJWluJSByb3duYW1lcyhwMmdfbWF0X3N1YikpICU+JQogIG11dGF0ZShzdHJhbmQgPSBpZmVsc2Uoc3RyYW5kID09IDEsICIrIiwgIi0iKSkgJT4lCiAgbXV0YXRlKHN0YXJ0X2Nvb3JkID0gaWZlbHNlKHN0cmFuZCA9PSAiKyIsIHN0YXJ0LCBlbmQpKSAlPiUgCiAgcmVuYW1lKGdlbmUgPSBuYW1lKSAjJT4lIEdSYW5nZXMoKQoKCiMgc3Vic2V0IGF0YWMgZ3JhbmdlcyAmIGdldCBtaWRkbGUgb2YgZWFjaCBwZWFrCnBvc19hdGFjX2dyYW5nZXMgPC0gYXRhY19ncmFuZ2VzICAlPiUgCiAgYXMuZGF0YS5mcmFtZSgpICU+JQogIG11dGF0ZShpZHhBVEFDID0gc2VxKG5yb3coLikpKSAlPiUgCiAgIyBncm91cF9ieShzZXFuYW1lcykgJT4lCiAgIyBtdXRhdGUoaWR4ID0gc2VxX2Fsb25nKHNlcW5hbWVzKSkgJT4lIAogICMgdW5ncm91cCAlPiUKICAjdGlkeXI6OnVuaXRlKGNocl9pZHgsIHNlcW5hbWVzLCBpZHgsIHJlbW92ZSA9IEZBTFNFLCBzZXAgPSAiXyIpICU+JSAKICBmaWx0ZXIoaWR4QVRBQyAlaW4lIGNvbG5hbWVzKHAyZ19tYXRfc3ViKSkgJT4lIAogIG11dGF0ZShtaWRkbGUgPSBzdGFydCArIDMwMCkgIyU+JSBHUmFuZ2VzKCkgCgojVE9ETzogRmlsdGVyIGZvciBnZW5lcyEKc3RvcGlmbm90KGxlbmd0aCh1bmlxdWUobGlua3MkaWR4QVRBQykpID09IGRpbShwb3NfYXRhY19ncmFuZ2VzKVtbMV1dKQpzdG9waWZub3QobGVuZ3RoKHVuaXF1ZShsaW5rcyRpZHhSTkEpKSA9PSBkaW0oZ2VuZV9hbm5vKVtbMV1dKQojcDJnX2ZpbHQgPC0gcDJnX29yaWdpbmFsICU+JSBhcy5kYXRhLmZyYW1lKCkgJT4lIGZpbHRlcihnZW5lICVpbiUgcm93bmFtZXMocDJnX21hdCkpCgoKICAjIGZpbmQgb3ZlcmxhcHBpbmcgcGVha3MgYW5kIGdlbmUgd2luZG93IGluIGNocm9tb3NvbWUtYXdhcmUgZmFzaGlvbgp0YWRfb3ZlcmxhcHNfZ2VuZXMgPC0gc3VwcHJlc3NXYXJuaW5ncyhmaW5kT3ZlcmxhcHMoZ2VuZV9hbm5vICU+JSBHUmFuZ2VzKCksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGFkX2JvdW5kYXJpZXMpKQoKcDEgPC0gdGFkX292ZXJsYXBzX2dlbmVzICU+JSBhcy5kYXRhLmZyYW1lKCkgJT4lCiAgZ3JvdXBfYnkoc3ViamVjdEhpdHMpICU+JQogIHN1bW1hcmlzZShuID0gbigpKSAlPiUgCiAgZ2dwbG90KCkgKyBnZW9tX2hpc3RvZ3JhbShhZXMoeCA9IG4pLCBiaW5zID0gMTAwKSArCiAgbGFicyh0aXRsZSA9ICJOdW1iZXIgb2YgaGlnaGx5IHZhcmlhYmxlIGdlbmVzIHdpdGhpbiBhIHRhZCBib3VuZGFyeSIsCiAgICAgICB4ID0gIm51bWJlciBvZiBnZW5lcy90YWQgYm91bmRhcnkiKQoKdGFkX292ZXJsYXBzX3BlYWtzIDwtIHN1cHByZXNzV2FybmluZ3MoZmluZE92ZXJsYXBzKHBvc19hdGFjX2dyYW5nZXMgJT4lIEdSYW5nZXMoKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0YWRfYm91bmRhcmllcykpCgoKcDIgPC0gdGFkX292ZXJsYXBzX3BlYWtzICU+JSBhcy5kYXRhLmZyYW1lKCkgJT4lCiAgZ3JvdXBfYnkoc3ViamVjdEhpdHMpICU+JQogIHN1bW1hcmlzZShuID0gbigpKSAlPiUgCiAgZ2dwbG90KCkgKyBnZW9tX2hpc3RvZ3JhbShhZXMoeCA9IG4pLCBiaW5zID0gMTAwKSArCiAgbGFicyh0aXRsZSA9ICJOdW1iZXIgb2YgcGVha3Mgd2l0aGluIGEgdGFkIGJvdW5kYXJ5IiwKICAgICAgIHggPSAibnVtYmVyIG9mIHBlYWtzL3RhZCBib3VuZGFyeSIpCgoKY293cGxvdDo6cGxvdF9ncmlkKHAxLCBwMiwgbmNvbCA9IDIpCmBgYAoKSG93IG1hbnkgcDJnIGxpbmtzIGFyZSB3aXRoaW4gdGFkIGJvdW5kYXJpZXM/CgpQZWFrLXRvLWdlbmUgbGlua3MgY29uc2lkZXJlZCBpbiBhYm92ZSBjb21wdXRhdGlvbnMKCkFsbCBwZWFrLXRvLWdlbmUgbGlua3MKCmBgYCN7ciwgZmlnLndpZHRoPTEyLCBmaWcuaGVpZ2h0PTV9CnJtKHBlYWtzKQpnYyhyZXNldCA9IFRSVUUpCgpwMmdfcG9zIDwtIHAyZyAlPiUgYXMuZGF0YS5mcmFtZSgpICU+JSBmaWx0ZXIoQ29ycmVsYXRpb24gPiAwKSAlPiUKICB1bml0ZShsaW5rLCBpZHhSTkEsIGlkeEFUQUMsIHNlcCA9ICIlIiwgcmVtb3ZlID0gRkFMU0UpCgpnZW5lX2Fubm9fYWxsIDwtIHJvd0RhdGEoZ2VuZV9leHByKSAlPiUgYXMuZGF0YS5mcmFtZSgpICU+JQogIG11dGF0ZShpZHhSTkEgPSBzZXEobnJvdyguKSkpICU+JSAKICBmaWx0ZXIoaWR4Uk5BICVpbiUgcDJnX3BvcyRpZHhSTkEpICU+JQogIG11dGF0ZShzdHJhbmQgPSBpZmVsc2Uoc3RyYW5kID09IDEsICIrIiwgIi0iKSkgJT4lCiAgbXV0YXRlKHN0YXJ0X2Nvb3JkID0gaWZlbHNlKHN0cmFuZCA9PSAiKyIsIHN0YXJ0LCBlbmQpKSAlPiUgCiAgcmVuYW1lKGdlbmUgPSBuYW1lKSAjJT4lIEdSYW5nZXMoKQoKIyBzdWJzZXQgYXRhYyBncmFuZ2VzICYgZ2V0IG1pZGRsZSBvZiBlYWNoIHBlYWsKcG9zX2F0YWNfZ3Jhbmdlc19hbGwgPC0gbWV0YWRhdGEocDJnKVtbMV1dICAlPiUgCiAgYXMuZGF0YS5mcmFtZSgpICU+JQogIG11dGF0ZShpZHhBVEFDID0gc2VxKG5yb3coLikpKSAlPiUgCiAgIyBncm91cF9ieShzZXFuYW1lcykgJT4lCiAgIyBtdXRhdGUoaWR4ID0gc2VxX2Fsb25nKHNlcW5hbWVzKSkgJT4lIAogICMgdW5ncm91cCAlPiUKICAjdGlkeXI6OnVuaXRlKGNocl9pZHgsIHNlcW5hbWVzLCBpZHgsIHJlbW92ZSA9IEZBTFNFLCBzZXAgPSAiXyIpICU+JSAKICBmaWx0ZXIoaWR4QVRBQyAlaW4lIHAyZ19wb3MkaWR4QVRBQykgJT4lIAogIG11dGF0ZShtaWRkbGUgPSBzdGFydCArIDMwMCkgIyU+JSBHUmFuZ2VzKCkgCgoKCiMgY29tYmluZSB0aGUgdGhyZWUgZGF0YWZyYW1lcwpwMmdfam9pbl9hbGwgPC0gbGVmdF9qb2luKHAyZ19wb3MsIGFzLmRhdGEuZnJhbWUocG9zX2F0YWNfZ3Jhbmdlc19hbGwpLAogICAgICAgICAgICAgICAgICAgICAgYnkgPSAiaWR4QVRBQyIpCnAyZ19qb2luX2FsbCA8LSBsZWZ0X2pvaW4ocDJnX2pvaW5fYWxsLCBhcy5kYXRhLmZyYW1lKGdlbmVfYW5ub19hbGwpLAogICAgICAgICAgICAgICAgICAgICAgYnkgPSAiaWR4Uk5BIiwgc3VmZml4ID0gYygiLmF0YWMiLCAiLnJuYSIpKQoKCnAyZ19qb2luX2FsbCA8LSBwMmdfam9pbl9hbGwgJT4lIAogIG11dGF0ZShkaXN0YW5jZSA9IGFicyhzdGFydF9jb29yZCAtIG1pZGRsZSkpCgoKCiAgIyBmaW5kIG92ZXJsYXBwaW5nIHBlYWtzIGFuZCBnZW5lIHdpbmRvdyBpbiBjaHJvbW9zb21lLWF3YXJlIGZhc2hpb24KdGFkX292ZXJsYXBzX2dlbmVzIDwtIHN1cHByZXNzV2FybmluZ3MoZmluZE92ZXJsYXBzKGdlbmVfYW5ub19hbGwgJT4lIEdSYW5nZXMoKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0YWRfYm91bmRhcmllcykpCgojIGdldCBnZW5lcyB3aGljaCBhcmUgbm90IGZvdW5kIHdpdGhpbiB0d28gVEFEIGJvdW5kYXJpZXMsIGJ1dCBvbmx5IHdpdGhpbiBvbmUKd2l0aGluX2dlbmVzIDwtICh0YWRfb3ZlcmxhcHNfZ2VuZXMgJT4lIAphcy5kYXRhLmZyYW1lKCkgJT4lIApncm91cF9ieShxdWVyeUhpdHMpICU+JQpzdW1tYXJpc2UobiA9IG4oKSkgJT4lIHVuZ3JvdXAoKSAlPiUKZmlsdGVyKG4gPCAyKSkkcXVlcnlIaXRzCgpwcmludChwYXN0ZTAoIk91dCBvZiAiLCBucm93KGdlbmVfYW5ub19hbGwpLCAiIGdlbmVzLCAiLCBsZW5ndGgodW5pcXVlKHF1ZXJ5SGl0cyh0YWRfb3ZlcmxhcHNfZ2VuZXMpKSksICIgZ2VuZXMgYXJlIHdpdGhpbiBUQUQgYm91bmRhcmllcy4gU29tZSBvZiB0aGVzZSBnZW5lcyBldmVuIHNwYW4gYWNyb3NzIFRBRCBib3VkbmFyaWVzLCBuYW1lbHkgIiwgYWJzKGxlbmd0aCh3aXRoaW5fZ2VuZXMpIC0gbGVuZ3RoKHVuaXF1ZShxdWVyeUhpdHModGFkX292ZXJsYXBzX2dlbmVzKSkpKSwgIi4iKSkKCiMgV2Ugb25seSBrZWVwIGdlbmVzIHdpdGhpbiBib3VuZGFyaWVzLCBidXQgbm90IGdlbmVzIGNyb3NzaW5nIGJvdW5kYXJpZXMKdGFkX292ZXJsYXBzX2dlbmVzIDwtIHRhZF9vdmVybGFwc19nZW5lcyAlPiUgYXMuZGF0YS5mcmFtZSAlPiUgCiAgZmlsdGVyKHF1ZXJ5SGl0cyAlaW4lIHdpdGhpbl9nZW5lcykgIyU+JSBTNFZlY3RvcnM6OmFzKCkKCiMgZ2V0IHBlYWtzIG92ZXJsYXBwaW5nIHdpdGggdGFkIGJvdW5kYXJpZXMKdGFkX292ZXJsYXBzX3BlYWtzIDwtIHN1cHByZXNzV2FybmluZ3MoZmluZE92ZXJsYXBzKHBvc19hdGFjX2dyYW5nZXNfYWxsICU+JSBHUmFuZ2VzKCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRhZF9ib3VuZGFyaWVzKSkKCiMgZmlsdGVyIGZvciBwZWFrcyBvdmVybGFwcGluZyB0YWQgYm91bmRhcmllcyB3aGljaCBhbHNvIGNvbnRhaW4gZ2VuZXMKdGFkX292ZXJsYXBzX3BlYWtzIDwtIHRhZF9vdmVybGFwc19wZWFrcyAlPiUgYXMuZGF0YS5mcmFtZSgpICU+JSAKICBmaWx0ZXIoc3ViamVjdEhpdHMgJWluJSB0YWRfb3ZlcmxhcHNfZ2VuZXMkc3ViamVjdEhpdHMpCgojIGNvbWJpbmUgdGFkIGJvdW5kYXJpZXMgd2hpY2ggY29udGFpbiBnZW5lcyBhbmQgcGVha3MKdGFkX2NvbWJpbmUgPC0gbGVmdF9qb2luKHRhZF9vdmVybGFwc19nZW5lcywgdGFkX292ZXJsYXBzX3BlYWtzLCAKICAgICAgICAgICAgICAgICAgICAgICAgIGNvcHkgPSBUUlVFLCBieSA9ICJzdWJqZWN0SGl0cyIsIHN1ZmZpeCA9IGMoIi5nZW5lIiwgIi5wZWFrIikpICU+JQogIHVuaXRlKGxpbmssIHF1ZXJ5SGl0cy5nZW5lLCBxdWVyeUhpdHMucGVhaywgc2VwID0gIiUiLCByZW1vdmUgPSBGQUxTRSkKCgpnZW5lcyA8LSBnZW5lX2Fubm9fYWxsW3RhZF9jb21iaW5lJHF1ZXJ5SGl0cy5nZW5lLCBdICU+JQogIG11dGF0ZSh0YWRfaW5kZXggPSB0YWRfY29tYmluZSRzdWJqZWN0SGl0cykKCnBlYWtfY29sbCA8LSBwb3NfYXRhY19ncmFuZ2VzX2FsbFt0YWRfY29tYmluZSRxdWVyeUhpdHMucGVhaywgXSAlPiUgCiAgbXV0YXRlKHRhZF9pbmRleCA9IHRhZF9jb21iaW5lJHN1YmplY3RIaXRzKQoKZ2VuZV9wZWFrX3RhZF9kZiA8LSBsZWZ0X2pvaW4oZ2VuZXMsIHBlYWtfY29sbCwgYnkgPSAidGFkX2luZGV4Iiwgc3VmZmljID0gYygiLmdlbmUiLCAiLnBlYWsiKSkgJT4lICB1bml0ZShwZWFrX2dlbmVfdGFkLCBnZW5lLCBpZHhBVEFDLCBzZXAgPSAiIyIsIHJlbW92ZSA9IEZBTFNFKQoKIyMjIHNvbWUgcGxvdHMKcDEgPC0gKHRhZF9vdmVybGFwc19wZWFrcyAgJT4lIGFzLmRhdGEuZnJhbWUoKSAlPiUgCiAgICAgICBncm91cF9ieShzdWJqZWN0SGl0cykgJT4lICMgZ2VuZSByZWdpb24KICAgICAgIHN1bW1hcml6ZShuID0gbigpKSAlPiUgIyBnZXQgbnVtYmVyIG9mIHBlYWtzIG92ZXJsYXBwaW5nIHdpdGggYSBnZW5lIHJlZ2lvbgogICAgICAgZ2dwbG90KCkgKyBnZW9tX2hpc3RvZ3JhbShhZXMoeCA9IG4pLCBiaW5zID0gMTAwLCBmaWxsPSIjNjliM2EyIikgKwogICAgICAgbGFicyh0aXRsZSA9ICJOdW1iZXIgb2YgcGVha3MgcGVyIHRhZCBib3VuZGFyeSwgcG9zaXRpdmUgcDJnIGxpbmtzIiwKICAgICAgICAgICB4ID0gIm51bWJlciBvZiBwZWFrcyIpKQoKcDIgPC0gKHRhZF9vdmVybGFwc19nZW5lcyAgJT4lIGFzLmRhdGEuZnJhbWUoKSAlPiUgCiAgICAgZ3JvdXBfYnkoc3ViamVjdEhpdHMpICU+JSAjIGdlbmUgcmVnaW9uCiAgICAgc3VtbWFyaXplKG4gPSBuKCkpICU+JSAjIGdldCBudW1iZXIgb2YgcGVha3Mgb3ZlcmxhcHBpbmcgd2l0aCBhIGdlbmUgcmVnaW9uCiAgICAgZ2dwbG90KCkgKyBnZW9tX2hpc3RvZ3JhbShhZXMoeCA9IG4pLCBiaW5zID0gMTAwLCBmaWxsPSIjNjliM2EyIikgKwogICAgIGxhYnModGl0bGUgPSAiTnVtYmVyIG9mIGdlbmVzIHBlciB0YWQgYm91bmRhcnksIHBvc2l0aXZlIHAyZyBsaW5rcyIsCiAgICAgICAgIHggPSAibnVtYmVyIG9mIGdlbmVzIikpCgpwcmludChjb3dwbG90OjpwbG90X2dyaWQocDEsIHAyLCBuY29sID0gMikpCgoKCnByaW50KHBhc3RlMCgiVGhlIG51bWJlciBvZiBwb3NpdGl2ZSBwZWFrLXRvLWdlbmUgbGlua3MgaXM6ICIsIGxlbmd0aChwMmdfcG9zJGxpbmspKSkKcHJpbnQocGFzdGUwKCAiVGhlIG51bWJlciBvZiBwb3NpdGl2ZSBwZWFrLXRvLWdlbmUgbGlua3Mgd2l0aGluIFRBRCBib3VuZGFyaWVzIGlzOiAiLCBsZW5ndGgodGFkX2NvbWJpbmUkbGluaykpKQoKcHJpbnQocGFzdGUwKCJUaGUgbnVtYmVyIG9mIHBvc2l0aXZlIHBlYWstdG8tZ2VuZSBsaW5rcyBvdXRzaWRlIFRBRCBib3VuZGFyaWVzIGlzOiAiLCBsZW5ndGgocDJnX3BvcyRsaW5rKSAtIGxlbmd0aCh0YWRfY29tYmluZSRsaW5rKSkpCgoKcHJpbnQocGFzdGUwKCJUaGUgcHJvcG9ydGlvbiBvZiBwZWFrLXRvLWdlbmUgbGlua3Mgd2l0aGluIFRBRCBib3VuZGFyaWVzIG91dCBvZiBhbGwgCiAgICAgICAgICAgICBwb3NpdGl2ZSBwZWFrLXRvLWdlbmUgbGlua3MgYWNyb3NzIHRoZSBlbnRpcmUgY2hyb21vc29tZSBpcyAiLAogICAgICAgICAgICAgcm91bmQobGVuZ3RoKHRhZF9jb21iaW5lJGxpbmspIC8gbGVuZ3RoKHAyZ19wb3MkbGluayksIDUpKSkKCgoKI2dncGxvdCgpICsgZ2VvbV9wb2ludChhZXMoeCA9IHAyZ19wb3MkaWR4QVRBQywgeSA9IHAyZ19wb3MkaWR4Uk5BKSkKCgpgYGAKCiMjIERpc3RhbmNlIHZzLiBDb3JyZWxhdGlvbgoKSGVyZSBJIHZpc3VhbGl6ZSB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gZGlzdGFuY2UgYmV0d2VlbiBwZWFrcyBhbmQgZ2VuZXMKYW5kIHRoZWlyIHJlc3BlY3RpdmUgY29ycmVsYXRpb24gdmFsdWVzIHVzaW5nIGFsbCBwb3NpdGl2ZSBsaW5rcyBvYnRhaW5lZCAKdXNpbmcgQXJjaFIgCgpgYGAje3J9CnAyZ19qb2luX2FsbCAlPiUgIAogIG11dGF0ZShiaW49Y3V0X3dpZHRoKGRpc3RhbmNlLCB3aWR0aD0xMDAwMDAsIGJvdW5kYXJ5PTApKSAlPiUKICBmaWx0ZXIoZGlzdGFuY2UgPCAxMDAwMDAwMCkgJT4lIAogIGdncGxvdCgpICsKICBnZW9tX2JveHBsb3QoYWVzKHggPSBiaW4sIHkgPSBDb3JyZWxhdGlvbiksIGZpbGw9IiM2OWIzYTIiKSArCiAgI2dlb21fdmxpbmUoeGludGVyY2VwdCAgPSAyNTAwMDAsIGNvbG9yID0gInJlZCIpICsKICBsYWJzKHRpdGxlID0gIlJlbGF0aW9uc2hpcCBiZXR3ZWVuIGRpc3RhbmNlIGFuZCBjb3JyZWxhdGlvbiBvZiBwMmcgbGlua3MsIDEwMGtiIGJpbnMiLAogICAgICAgeCA9ICJEaXN0YW5jZSBiZXR3ZWVuIHBlYWtzIGFuZCBnZW5lcyB3aXRoaW4gMjUwa2IiLCB5ID0gIkNvcnJlbGF0aW9uIikgKyAKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCB2anVzdCA9IDAuNSwgaGp1c3Q9MSkpCgpgYGAKCgpgYGB7ciwgZmlnLndpZHRoPTh9Cgpjb2xQYWxldHRlX2NlbGx0eXBlcyA9IGMoJyM1MzJDOEEnLAogJyNjMTlmNzAnLAogJyNmOWRlY2YnLAogJyNjOWE5OTcnLAogJyNCNTFEOEQnLAogJyMzRjg0QUEnLAogJyM5ZTY3NjInLAogJyMzNTRFMjMnLAogJyNGMzk3QzAnLAogJyNmZjg5MWMnLAogJyM2MzU1NDcnLAogJyNDNzIyMjgnLAogJyNmNzkwODMnLAogJyNFRjRFMjInLAogJyM5ODk4OTgnLAogJyM3RjY4NzQnLAogJyM4ODcwYWQnLAogJyM2NDdhNGYnLAogJyNFRjVBOUQnLAogJyNGQkJFOTInLAogJyMxMzk5OTInLAogJyNjYzc4MTgnLAogJyNERkNERTQnLAogJyM4RUM3OTInLAogJyNDNTk0QkYnLAogJyNDM0MzODgnLAogJyMwRjRBOUMnLAogJyNGQUNCMTInLAogJyM4REI1Q0UnLAogJyMxQTFBMUEnLAogJyNDOUVCRkInLAogJyNEQUJFOTknLAogJyM2NUE4M0UnLAogJyMwMDU1NzknLAogJyNDREUwODgnLAogJyNmN2Y3OWUnLAogJyNGNkJGQ0InKQoKdGFkX2JvdW5kYXJpZXMgJT4lIGFzLmRhdGEuZnJhbWUoKSAlPiUgZ3JvdXBfYnkoc2VxbmFtZXMpICU+JSAKICBzdW1tYXJpc2UobiA9IG4oKSkgJT4lIHVuZ3JvdXAoKSAlPiUgIAogIGdncGxvdCgpICsgZ2VvbV9jb2woYWVzKHggPSBzZXFuYW1lcywgeSA9IG4sIGZpbGwgPSBzZXFuYW1lcyksIGFscGhhID0gLjcsICkgKyMsIHBvc2l0aW9uID0gImRvZGdlIikKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiTm9uZSIpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjb2xQYWxldHRlX2NlbGx0eXBlcykgKwogIGxhYnMoeSA9ICJudW1iZXIgb2YgdGFkIGJvdW5kYXJpZXMiKQoKYGBgCgoKVE9ETzogU2hvdWxkIEkgYWxzbyByZW1vdmUgcGVha3Mgd2hpY2ggYXJlIGFjcm9zcyBUQUQgYm91bmRhcmllcz8KCmBgYHtyLCBmaWcud2lkdGg9MTUsIGZpZy5oZWlnaHQ9MTB9CiMgQXMgaW5wdXQgZm9yIHRoaXMgZnVuY3Rpb24gaXQgaXMgYmVzdCB0byB1c2Ugb25seSB0aGUgbW9zdCBoaWdobHkgdmFyaWFibGUgZ2VuZXMKdGFkX2JvdW5kYXJpZXNfcDJnX3Njb3JlcyA8LSBmdW5jdGlvbihwMmdfbWF0X3N1YiwgcGVha19tYXQsIGxpbmtzLCBwMmdfb3JpZ2luYWwsIGdlbmVfZXhwciwgdGFkX2JvdW5kYXJpZXMpewogIGF0YWNfZ3JhbmdlcyA8LSBtZXRhZGF0YShwMmdfb3JpZ2luYWwpW1sxXV0KICAjcm5hX2dyYW5nZXMgPC0gbWV0YWRhdGEocDJnX29yaWdpbmFsKVtbMl1dCiAgZ2VuZV9hbm5vIDwtIHJvd0RhdGEoZ2VuZV9leHByKQogIAogICMgY3JlYXRlIGdlbmUgYW5ub3RhdGlvbnMgd2l0aCBzdGFydCBjb29yZGluYXRlIG9mIGVhY2ggZ2VuZQogICMgc3Vic2V0IHRvIGNvbnRhaW4gb25seSBnZW5lcyB3aGljaCBhcmUgaW5jbHVkZWQgaW4gb3VyIHBlYWsyZ2VuZSBtYXRyaXgKICBnZW5lX2Fubm8gPC0gZ2VuZV9hbm5vICU+JSBhcy5kYXRhLmZyYW1lKCkgJT4lCiAgICBtdXRhdGUoaWR4Uk5BID0gc2VxKG5yb3coLikpKSAlPiUgCiAgICBmaWx0ZXIobmFtZSAlaW4lIHJvd25hbWVzKHAyZ19tYXRfc3ViKSkgJT4lCiAgICBtdXRhdGUoc3RyYW5kID0gaWZlbHNlKHN0cmFuZCA9PSAxLCAiKyIsICItIikpICU+JQogICAgbXV0YXRlKHN0YXJ0X2Nvb3JkID0gaWZlbHNlKHN0cmFuZCA9PSAiKyIsIHN0YXJ0LCBlbmQpKSAlPiUgCiAgICByZW5hbWUoZ2VuZSA9IG5hbWUpICMlPiUgR1JhbmdlcygpCgoKICAjIHN1YnNldCBhdGFjIGdyYW5nZXMgJiBnZXQgbWlkZGxlIG9mIGVhY2ggcGVhawogIHBvc19hdGFjX2dyYW5nZXMgPC0gYXRhY19ncmFuZ2VzICAlPiUgCiAgICBhcy5kYXRhLmZyYW1lKCkgJT4lCiAgICBtdXRhdGUoaWR4QVRBQyA9IHNlcShucm93KC4pKSkgJT4lIAogICAgIyBncm91cF9ieShzZXFuYW1lcykgJT4lCiAgICAjIG11dGF0ZShpZHggPSBzZXFfYWxvbmcoc2VxbmFtZXMpKSAlPiUgCiAgICAjIHVuZ3JvdXAgJT4lCiAgICAjdGlkeXI6OnVuaXRlKGNocl9pZHgsIHNlcW5hbWVzLCBpZHgsIHJlbW92ZSA9IEZBTFNFLCBzZXAgPSAiXyIpICU+JSAKICAgIGZpbHRlcihpZHhBVEFDICVpbiUgY29sbmFtZXMocDJnX21hdF9zdWIpKSAlPiUgCiAgICBtdXRhdGUobWlkZGxlID0gc3RhcnQgKyAzMDApICMlPiUgR1JhbmdlcygpIAogIAogICNUT0RPOiBGaWx0ZXIgZm9yIGdlbmVzIQogIHN0b3BpZm5vdChsZW5ndGgodW5pcXVlKGxpbmtzJGlkeEFUQUMpKSA9PSBkaW0ocG9zX2F0YWNfZ3JhbmdlcylbWzFdXSkKICBzdG9waWZub3QobGVuZ3RoKHVuaXF1ZShsaW5rcyRpZHhSTkEpKSA9PSBkaW0oZ2VuZV9hbm5vKVtbMV1dKQogICNwMmdfZmlsdCA8LSBwMmdfb3JpZ2luYWwgJT4lIGFzLmRhdGEuZnJhbWUoKSAlPiUgZmlsdGVyKGdlbmUgJWluJSByb3duYW1lcyhwMmdfbWF0KSkKICAKICAKICAgICMgZmluZCBvdmVybGFwcGluZyBwZWFrcyBhbmQgZ2VuZSB3aW5kb3cgaW4gY2hyb21vc29tZS1hd2FyZSBmYXNoaW9uCiAgdGFkX292ZXJsYXBzX2dlbmVzIDwtIHN1cHByZXNzV2FybmluZ3MoZmluZE92ZXJsYXBzKGdlbmVfYW5ubyAlPiUgR1JhbmdlcygpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGFkX2JvdW5kYXJpZXMpKQogIAogICMgZ2V0IGdlbmVzIHdoaWNoIGFyZSBub3QgZm91bmQgd2l0aGluIHR3byBUQUQgYm91bmRhcmllcywgYnV0IG9ubHkgd2l0aGluIG9uZQogIHdpdGhpbl9nZW5lcyA8LSAodGFkX292ZXJsYXBzX2dlbmVzICU+JSAKICBhcy5kYXRhLmZyYW1lKCkgJT4lIAogIGdyb3VwX2J5KHF1ZXJ5SGl0cykgJT4lCiAgc3VtbWFyaXNlKG4gPSBuKCkpICU+JSB1bmdyb3VwKCkgJT4lCiAgZmlsdGVyKG4gPCAyKSkkcXVlcnlIaXRzCgogIHByaW50KHBhc3RlMCgiT3V0IG9mICIsIG5yb3coZ2VuZV9hbm5vKSwgIiBnZW5lcywgIiwgbGVuZ3RoKHVuaXF1ZShxdWVyeUhpdHModGFkX292ZXJsYXBzX2dlbmVzKSkpLCAiIGdlbmVzIGFyZSB3aXRoaW4gVEFEIGJvdW5kYXJpZXMuIFNvbWUgb2YgdGhlc2UgZ2VuZXMgZXZlbiBzcGFuIGFjcm9zcyBUQUQgYm91ZG5hcmllcywgbmFtZWx5ICIsIGxlbmd0aCh3aXRoaW5fZ2VuZXMpLCAiLiIpKQogIAogICMgV2Ugb25seSBrZWVwIGdlbmVzIHdpdGhpbiBib3VuZGFyaWVzLCBidXQgbm90IGdlbmVzIGNyb3NzaW5nIGJvdW5kYXJpZXMKICB0YWRfb3ZlcmxhcHNfZ2VuZXMgPC0gdGFkX292ZXJsYXBzX2dlbmVzICU+JSBhcy5kYXRhLmZyYW1lICU+JSAKICAgIGZpbHRlcihxdWVyeUhpdHMgJWluJSB3aXRoaW5fZ2VuZXMpICMlPiUgUzRWZWN0b3JzOjphcygpCiAgCiAgIyBnZXQgcGVha3Mgb3ZlcmxhcHBpbmcgd2l0aCB0YWQgYm91bmRhcmllcwogIHRhZF9vdmVybGFwc19wZWFrcyA8LSBzdXBwcmVzc1dhcm5pbmdzKGZpbmRPdmVybGFwcyhwb3NfYXRhY19ncmFuZ2VzICU+JSBHUmFuZ2VzKCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGFkX2JvdW5kYXJpZXMpKQogIAogICMgZmlsdGVyIGZvciBwZWFrcyBvdmVybGFwcGluZyB0YWQgYm91bmRhcmllcyB3aGljaCBhbHNvIGNvbnRhaW4gZ2VuZXMKICB0YWRfb3ZlcmxhcHNfcGVha3MgPC0gdGFkX292ZXJsYXBzX3BlYWtzICU+JSBhcy5kYXRhLmZyYW1lKCkgJT4lIAogICAgZmlsdGVyKHN1YmplY3RIaXRzICVpbiUgdGFkX292ZXJsYXBzX2dlbmVzJHN1YmplY3RIaXRzKQogIAogICMgY29tYmluZSB0YWQgYm91bmRhcmllcyB3aGljaCBjb250YWluIGdlbmVzIGFuZCBwZWFrcwogIHRhZF9jb21iaW5lIDwtIGxlZnRfam9pbih0YWRfb3ZlcmxhcHNfZ2VuZXMsIHRhZF9vdmVybGFwc19wZWFrcywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvcHkgPSBUUlVFLCBieSA9ICJzdWJqZWN0SGl0cyIsIHN1ZmZpeCA9IGMoIi5nZW5lIiwgIi5wZWFrIikpCiAgCiAgCiAgZ2VuZXMgPC0gZ2VuZV9hbm5vW3RhZF9jb21iaW5lJHF1ZXJ5SGl0cy5nZW5lLCBdICU+JQogICAgbXV0YXRlKHRhZF9pbmRleCA9IHRhZF9jb21iaW5lJHN1YmplY3RIaXRzKQogIAogIHBlYWtfY29sbCA8LSBwb3NfYXRhY19ncmFuZ2VzW3RhZF9jb21iaW5lJHF1ZXJ5SGl0cy5wZWFrLCBdICU+JSAKICAgIG11dGF0ZSh0YWRfaW5kZXggPSB0YWRfY29tYmluZSRzdWJqZWN0SGl0cykKICAKICBnZW5lX3BlYWtfdGFkX2RmIDwtIGxlZnRfam9pbihnZW5lcywgcGVha19jb2xsLCBieSA9ICJ0YWRfaW5kZXgiLCBzdWZmaWMgPSBjKCIuZ2VuZSIsICIucGVhayIpKSAlPiUgIHVuaXRlKHBlYWtfZ2VuZV90YWQsIGdlbmUsIGlkeEFUQUMsIHNlcCA9ICIjIiwgcmVtb3ZlID0gRkFMU0UpCgoKICAKICAjIyMgc29tZSBwbG90cwogIHAxIDwtICh0YWRfb3ZlcmxhcHNfcGVha3MgICU+JSBhcy5kYXRhLmZyYW1lKCkgJT4lIAogICAgICAgICBncm91cF9ieShzdWJqZWN0SGl0cykgJT4lICMgZ2VuZSByZWdpb24KICAgICAgICAgc3VtbWFyaXplKG4gPSBuKCkpICU+JSAjIGdldCBudW1iZXIgb2YgcGVha3Mgb3ZlcmxhcHBpbmcgd2l0aCBhIGdlbmUgcmVnaW9uCiAgICAgICAgIGdncGxvdCgpICsgZ2VvbV9oaXN0b2dyYW0oYWVzKHggPSBuKSwgYmlucyA9IDEwMCwgZmlsbD0iIzY5YjNhMiIpICsKICAgICAgICAgbGFicyh0aXRsZSA9ICJOdW1iZXIgb2YgcGVha3MgcGVyIHRhZCBib3VuZGFyeSIsCiAgICAgICAgICAgICB4ID0gIm51bWJlciBvZiBwZWFrcyIpKQogIAogIHAyIDwtICh0YWRfb3ZlcmxhcHNfZ2VuZXMgICU+JSBhcy5kYXRhLmZyYW1lKCkgJT4lIAogICAgICAgZ3JvdXBfYnkoc3ViamVjdEhpdHMpICU+JSAjIGdlbmUgcmVnaW9uCiAgICAgICBzdW1tYXJpemUobiA9IG4oKSkgJT4lICMgZ2V0IG51bWJlciBvZiBwZWFrcyBvdmVybGFwcGluZyB3aXRoIGEgZ2VuZSByZWdpb24KICAgICAgIGdncGxvdCgpICsgZ2VvbV9oaXN0b2dyYW0oYWVzKHggPSBuKSwgYmlucyA9IDEwMCwgZmlsbD0iIzY5YjNhMiIpICsKICAgICAgIGxhYnModGl0bGUgPSAiTnVtYmVyIG9mIGh2ZyBnZW5lcyBwZXIgdGFkIGJvdW5kYXJ5IiwKICAgICAgICAgICB4ID0gIm51bWJlciBvZiBnZW5lcyIpKQogIAogIHByaW50KGNvd3Bsb3Q6OnBsb3RfZ3JpZChwMSwgcDIsIG5jb2wgPSAyKSkKICAKICAKICAjIGNvbWJpbmUgdGhlIGFubm90YXRpb24gZGF0YWZyYW1lIHdpdGggdGhlIHAyZyBsaW5rcyBkYXRhZnJhbWUKICBwMmdfam9pbiA8LSBsZWZ0X2pvaW4obGlua3MsIGFzLmRhdGEuZnJhbWUocG9zX2F0YWNfZ3JhbmdlcyksCiAgICAgICAgICAgICAgICAgICAgICAgIGJ5ID0gImlkeEFUQUMiKQogIHAyZ19qb2luIDwtIGxlZnRfam9pbihwMmdfam9pbiwgYXMuZGF0YS5mcmFtZShnZW5lX2Fubm8pLAogICAgICAgICAgICAgICAgICAgICAgICBieSA9ICJpZHhSTkEiLCBzdWZmaXggPSBjKCIuYXRhYyIsICIucm5hIikpCgogICMgY29tcHV0ZSBkaXN0YW5jZSAKICBwMmdfam9pbiA8LSBwMmdfam9pbiAlPiUgCiAgICBtdXRhdGUoZGlzdGFuY2UgPSBhYnMoc3RhcnRfY29vcmQgLSBtaWRkbGUpKQogIAogICMgZmlsdGVyIGZvciB0aGUgcDJnIGxpbmtzIHdpdGhpbiB0YWQgYm91bmRhcmllcwogIGNvcnJfdGFkX2JvdW5kYXJ5IDwtIHAyZ19qb2luICU+JSAKICAgIHVuaXRlKHBlYWtfZ2VuZV90YWQsIGdlbmUsIGlkeEFUQUMsIHNlcCA9ICIjIiwgcmVtb3ZlID0gRkFMU0UpICU+JSAKICAgIGZpbHRlcihwZWFrX2dlbmVfdGFkICVpbiUgZ2VuZV9wZWFrX3RhZF9kZiRwZWFrX2dlbmVfdGFkKQoKICAjIyMgUExPVFMKICAKICBwMSA8LSBjb3JyX3RhZF9ib3VuZGFyeSAlPiUgCiAgICBnZ3Bsb3QoKSArCiAgICBnZW9tX2hpc3RvZ3JhbShhZXMoeCA9IENvcnJlbGF0aW9uKSwgYmlucyA9IDIwMCwgZmlsbCA9ICIjNjliM2EyIikgKwogICAgbGFicyh0aXRsZSA9ICJDb3JyZWxhdGlvbiB2YWx1ZXMgb2YgcGVhay10by1nZW5lIGxpbmtzIGZvdW5kIHdpdGhpbiB0YWQgYm91bmRhcmllcyIpCiAgCiAgcDIgPC0gY29ycl90YWRfYm91bmRhcnkgJT4lIAogICAgZ2dwbG90KCkgKwogICAgZ2VvbV9oaXN0b2dyYW0oYWVzKHggPSBkaXN0YW5jZSksIGJpbnMgPSAyMDAsIGZpbGwgPSAiIzY5YjNhMiIpICsKICAgIGxhYnModGl0bGUgPSAiRGlzdGFuY2UgYmV0d2VlbiBwZWFrcyBhbmQgVFNTIHdpdGhpbiB0YWQgYm91bmRhcnkiKQogIAogIHAzIDwtIGNvcnJfdGFkX2JvdW5kYXJ5ICU+JSAKICAgIG11dGF0ZShiaW4gPSBjdXRfd2lkdGgoZGlzdGFuY2UsIHdpZHRoPTEwMDAwMCwgYm91bmRhcnk9MCkpICU+JSAKICAgIGdncGxvdCgpICsKICAgIGdlb21fYm94cGxvdChhZXMoeCA9IGJpbiwgeSA9IENvcnJlbGF0aW9uKSwgZmlsbCA9ICIjNjliM2EyIikgKwogICAgbGFicyh0aXRsZSA9ICJEaXN0YW5jZSBhbmQgQ29ycmVsYXRpb24gd2l0aGluIHRhZCBib3VuZGFyeSwgMTAwa2IgYmlucyIsCiAgICAgICAgIHggPSAiRGlzdGFuY2UgKDEwMGtiIGJpbnMpIikgKwogICAgc2NhbGVfeF9kaXNjcmV0ZShndWlkZSA9IGd1aWRlX2F4aXMoYW5nbGUgPSA5MCkpIAoKICBwcmludChjb3dwbG90OjpwbG90X2dyaWQocDEsIHAyLCBwMywgbmNvbCA9IDEpKQogIAoKCiAgCiAgIyMjIyBQTE9UCiAgcDIgPC0gY29ycl90YWRfYm91bmRhcnkgJT4lIGdncGxvdCgpICsKICAgIGdlb21faGlzdG9ncmFtKGFlcyh4ID0gZGlzdGFuY2UpLCBiaW5zID0gNTAwKSArIAogICAgc2NhbGVfeV9sb2cxMCgpICsKICAgIGxhYnModGl0bGUgPSAiUmVsYXRpdmUgZGlzdGFuY2Ugb2YgcGVha3MgdG8gVFNTIHdpdGhpbiB0YWQgYm91bmRhcmllcyIsCiAgICAgICAgIHggPSAiUmVsYXRpdmUgZGlzdGFuY2UgdG8gVFNTIiwgeSA9ICJsb2cxMChjb3VudCkiKSArIAogICAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gYygxMDAwMDAsIC0xMDAwMDApLCBjb2xvciA9ICJyZWQiKQogIAogIHByaW50KHAyKQogICNjb3dwbG90OjpwbG90X2dyaWQocDEsIHAyLCBuY29sID0gMSkKICAKICAKICAKICBwMmdfbGlua3NfdGFkIDwtIE1hdHJpeDo6c3BhcnNlTWF0cml4KAogICAgICBpID0gY29ycl90YWRfYm91bmRhcnkkaWR4Uk5BLCAKICAgICAgaiA9IGNvcnJfdGFkX2JvdW5kYXJ5JGlkeEFUQUMsIAogICAgICB4ID0gY29ycl90YWRfYm91bmRhcnkkQ29ycmVsYXRpb24sIAogICAgICBkaW1zID0gYyhucm93KGV4cHJfbWF0KSwgbnJvdyhwZWFrX21hdCkpLAogICAgICBkaW1uYW1lcyA9IGxpc3Qocm93bmFtZXMoZXhwcl9tYXQpLHJvd25hbWVzKHBlYWtfbWF0KSkKICAgICkKICAKICAKICBwcmludChwYXN0ZTAoIlRoZSBtYXhpbXVtIHZhbHVlIGlzOiAiLCBtYXgocDJnX2xpbmtzX3RhZCksICIsIHRoZSBtaW51bSB2YWx1ZSBpczogIiwgbWluKHAyZ19saW5rc190YWQpICkpCiAgCiAgCiAgCiAgcDJnX2xpbmtzX3RhZCA8LSBwMmdfbGlua3NfdGFkW3Jvd1N1bXMocDJnX2xpbmtzX3RhZCkgIT0gMCwgXQogIHAyZ19saW5rc190YWQgPC0gcDJnX2xpbmtzX3RhZFssIGNvbFN1bXMocDJnX2xpbmtzX3RhZCkgIT0gMF0KICAKICBwcmludChwYXN0ZTAoIkFmdGVyIHJlbW92aW5nIGFueSByb3dzIGFuZCBjb2x1bXNuIHdoaWNoIGRvIG5vdCBjb250YWluIGFueSBsaW5rcyB3ZSBhcmUgbGVmdCB3aXRoICIsIG5yb3cocDJnX2xpbmtzX3RhZCksICIgZ2VuZXMgYW5kICIsIG5jb2wocDJnX2xpbmtzX3RhZCksICIgcGVha3MuIikpCiAgCiAgCiAgIyBDb21wdXRlIGdlbmUgYWN0aXZpdHkgc2NvcmVzCiAgdGFkX3Njb3JlcyA8LSBnZW5lX2FjdGl2aXR5X3Njb3JlcyhwZWFrX21hdF9zdWJbY29sbmFtZXMocDJnX2xpbmtzX3RhZCksIF0sIHAyZ19saW5rc190YWQpCiAgCiAgcmV0dXJuKHRhZF9zY29yZXMpIAp9CmdjKHJlc2V0ID0gVFJVRSkKYGBgCgpgYGB7ciwgZmlnLndpZHRoPTgsIGZpZy5oZWlnaHQ9OH0KdGFkX3Njb3JlcyA8LSB0YWRfYm91bmRhcmllc19wMmdfc2NvcmVzKHAyZ19tYXRfc3ViID0gcDJnX21hdF9zdWIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwZWFrX21hdCA9IHBlYWtfbWF0LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGlua3MgPSBsaW5rcywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwMmdfb3JpZ2luYWwgPSBwMmcsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ2VuZV9leHByID0gZ2VuZV9leHByLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGFkX2JvdW5kYXJpZXMgPSB0YWRfYm91bmRhcmllcykKCmdjKHJlc2V0ID0gVFJVRSkKYGBgCgoKYGBge3IsIGZpZy53aWR0aD04LCBmaWcuaGVpZ2h0PTV9CmdlbmVfd2luZG93X2FnZyA8LSBjcmVhdGVfc2VhY2VsbF9hZ2dyZWdhdGVzKHRhZF9zY29yZXMsIHNlYWNlbGxzKQpnZW5lX3dpbmRvd19jb3JyIDwtIHJvd3dpc2VfY29ycmVsYXRpb25zKHNlYWNlbGxfcm5hX2FnZywgZ2VuZV93aW5kb3dfYWdnLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5hbWUgPSAiR2VuZSB3aW5kb3cgYXJvdW5kIFRTUyIpCgpnZW5lX3dpbmRvd19jb3JyW1syXV0KCgpnZ3Bsb3QoKSArCiAgZ2VvbV9wb2ludChhZXMoeSA9IHNlYWNlbGxfY29ycl9hcmNocltbMV1dW25hbWVzKGdlbmVfd2luZG93X2NvcnJbWzFdXSldLCAKICAgICAgICAgICAgICAgICB4ID1nZW5lX3dpbmRvd19jb3JyW1sxXV0pKSAgKwogIGdlb21fZGVuc2l0eV8yZF9maWxsZWQoYWVzKHkgPSBzZWFjZWxsX2NvcnJfYXJjaHJbWzFdXVtuYW1lcyhnZW5lX3dpbmRvd19jb3JyW1sxXV0pXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICB4ID0gZ2VuZV93aW5kb3dfY29ycltbMV1dKSwgYWxwaGEgPSAwLjUpICsKICBnZW9tX2xpbmUoYWVzKHggPSBzZWFjZWxsX2NvcnJfYXJjaHJbWzFdXVtuYW1lcyhnZW5lX3dpbmRvd19jb3JyW1sxXV0pXSwgCiAgICAgICAgICAgICAgICB5ID0gc2VhY2VsbF9jb3JyX2FyY2hyW1sxXV1bbmFtZXMoZ2VuZV93aW5kb3dfY29ycltbMV1dKV0sIAogICAgICAgICAgICAgICAgY29sb3IgPSAicmVkIikpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiTm9uZSIpICArCiAgbGFicyh4ID0gIkNvcnJlbGF0aW9uIGJldHdlZW4gZ2VuZSBleHByZXNzaW9uIGFuZCBwMmcgYWN0aXZpdHkgc2NvcmVzLCB0YWQgYm91bmRhcnkiLAogICAgICAgIHkgPSAiQ29ycmVsYXRpb24gYmV0d2VlbiBnZW5lIGV4cHJlc3Npb24gYW5kIEFyY2hSIGdlbmUgYWN0aXZpdHkgc2NvcmVzIiwgCiAgICAgICB0aXRsZSA9ICJQZWFrLXRvLWdlbmUgbGlua3Mgd2l0aGluIHRhZCBib3VuZGFyaWVzIikKCmBgYApgYGB7ciwgZmlnLndpZHRoPTgsIGZpZy5oZWlnaHQ9NX0KZ2dwbG90KCkgKwogIGdlb21fcG9pbnQoYWVzKHkgPSBzZWFjZWxsX2NvcnJfcDJnW1sxXV1bbmFtZXMoZ2VuZV93aW5kb3dfY29ycltbMV1dKV0sIAogICAgICAgICAgICAgICAgIHggPWdlbmVfd2luZG93X2NvcnJbWzFdXSkpICArCiAgZ2VvbV9kZW5zaXR5XzJkX2ZpbGxlZChhZXMoeSA9IHNlYWNlbGxfY29ycl9wMmdbWzFdXVtuYW1lcyhnZW5lX3dpbmRvd19jb3JyW1sxXV0pXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICB4ID0gZ2VuZV93aW5kb3dfY29ycltbMV1dKSwgYWxwaGEgPSAwLjUpICsKICBnZW9tX2xpbmUoYWVzKHggPSBzZWFjZWxsX2NvcnJfcDJnW1sxXV1bbmFtZXMoZ2VuZV93aW5kb3dfY29ycltbMV1dKV0sIAogICAgICAgICAgICAgICAgeSA9IHNlYWNlbGxfY29ycl9wMmdbWzFdXVtuYW1lcyhnZW5lX3dpbmRvd19jb3JyW1sxXV0pXSwgCiAgICAgICAgICAgICAgICBjb2xvciA9ICJyZWQiKSkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJOb25lIikgICsKICBsYWJzKHggPSAiQ29ycmVsYXRpb24sIHRhZCBib3VuZGFyaWVzIiwKICAgICAgICB5ID0gIkNvcnJlbGF0aW9uLCBhbGwgcDJnIGxpbmtzICIsIAogICAgICAgdGl0bGUgPSAiUGVhay10by1nZW5lIGxpbmtzIHdpdGhpbiB0YWQgYm91bmRhcmllcyB2cy4gYWxsIGxpbmtzIikKYGBgCgoKCgoKCiMgQWRhcHRlZCBBcmNociBHZW5lIEFjdGl2aXR5IFNjb3JlIGZ1bmN0aW9uCgpBcmNoUiBwcm92aWRlcyBhIGZ1bmN0aW9uIHRvIGNvbXB1dGUgZ2VuZSBhY3Rpdml0eSBzY29yZXMgYmFzZWQgb24gYWNjZXNzaWJpbGl0eSAKaW4gdGhlIHJlZ2lvbnMgYXJvdW5kIHRoZSBnZW5lLiBGb3IgdGhpcyBhIHRpbGUgbWF0cml4IGlzIHVzZWQuIFRoaXMgdGlsZSBtYXRyaXggCmlzIGEgbWF0cml4IHdoZXJlIHRoZSBnZW5vbWUgaXMgZGl2aWRlZCBpbnRvIGJpbnMgb2YgNTAwYnAuIElmIHRoZXJlIGlzIGEKVG41IGluc2VydGlvbiBpbiBhIGJpbiB0aGUgZW50cnkgd2lsbCBiZSAxLCBpZiB0aGVyZSBpcyBubyBpbnNlcnRpb24gdGhlIGVudHJ5CndpbGwgYmUgMC4gSW1wb3J0YW50bHksIHRoZXkgY29tcGFyZWQgdGhlaXIgZnVuY3Rpb24gdG8gNTIgb3RoZXIgZnVuY3Rpb25zCmFuZCBmb3VuZCB0aGVpciBvd24gZnVuY3Rpb24gdG8gYmUgdGhlIGJlc3QgcGVyZm9ybWluZy4gCgpIZXJlIEkgdHJpZWQgdG8gYmV0dGVyIHVuZGVyc3RhbmQgaG93IHRoaXMgZnVuY3Rpb24gd29ya3MgYW5kIGNoYW5nZWQgdGhlIHNvdXJjZSAKY29kZSBvZiB0aGUgQXJjaFIgZnVuY3Rpb24gdG8gYWxzbyB0YWtlIHBlYWsgbWF0cml4IGFzIGlucHV0IGFuZCBjb21wdXRlIHRoZSBnZW5lIGFjdGl2aXR5IApiYXNlZCBvbiBwZWFrcywgcmF0aGVyIHRoYW4gYmFzZWQgb24gdGlsZXMuIEFkZGl0aW9uYWxseSwgSSBhZGFwdGVkIHRoZSBmdW5jaXRvbiAKaW4gYSB3YXkgc3VjaCB0aGF0IGl0IHRha2VzIHRhZCBib3VuZGFyaWVzIGFzIGlucHV0IGFuZCB1c2VzIGFsbCBwZWFrcyB3aGljaCBhcmUgCndpdGhpbiB0aGUgc2FtZSB0YWQgYm91bmRhcnkgYXMgYSBnZW5lIHRvIGNvbXB1dGUgdGhlIGFjdGl2aXR5IHNjb3Jlcy4gCgpUaGVyZSBhcmUgdHdvIGRpZmZlcmVudCBvcHRpb25zIGZvciBjb21wdXRpbmcgZ2VuZSBhY3Rpdml0eSBzY29yZXMgaW4gQXJjaFIuIEZpcnN0LAp3ZSBjYW4gdXNlIHRoZSBUU1MgYW5kIGNyZWF0ZSBhIGdlbmUgd2luZG93IGFyb3VuZCBpdCAoKy8tIDEwMGtwIG9mIFRTUykuIEFsbCAKaW5zZXJ0aW9ucyBmb3VuZCB3aXRoaW4gdGlsZXMgd2l0aGluIHRoaXMgZ2VuZSB3aW5kb3cgd2lsbCBiZSBhY2N1bXVsYXRlZCBmb3IgdGhlCmdlbmUgYWN0aXZpdHkgc2NvcmVzLiBJZiB3ZSBzZXQgdGhlIG9wdGlvbiAndXNlR2VuZUJvdW5kYXJpZXM9VFJVRScgdGhlbiB3ZSB3aWxsIAptYWtlIHN1cmUgdGhhdCBubyByZWdpb25zIG92ZXJsYXAgYmV0d2VlbiBhbnkgdHdvIGdlbmVzLiBJZiB0aGUgZ2VuZSB3aW5kb3cgb2YgCm9uZSBnZW5lIG92ZXJsYXBzIHdpdGggdGhlIGdlbmUgd2luZG93IG9mIGFub3RoZXIgZ2VuZSwgdGhvc2UgdGlsZXMgYXJlIG5vdCAKY29uc2lkZXJlZCBhbnltb3JlLiBUaGUgZGlzYWR2YW50YWdlIG9mIHRoaXMgYXBwcm9hYyBpcyB0aGF0IGdlbmVzIGNhbiBiZSB2ZXJ5IApsYXJnZSAoPjEwMGJwKSwgbWVhbmluZyB0aGF0IGluIHNvbWUgY2FzZXMgdGhlIDEwMGtwIGV4dGVuc2lvbiBkb3duc3RyZWFtIG9mIHRoZQpUU1Mgd291bGQgbm90IGV2ZW4gY29udGFpbiB0aGUgZW50aXJlIGdlbmUgYm9keS4gCgpTZWNvbmQsIHdlIGNhbiB1c2UgdGhlIGVudGlyZSBnZW5lIGJvZHkgYW5kIGV4dGVuZCB0aGUgZ2VuZSB3aW5kb3cgYmV5b25kIHRoZSBzdGFydAphbmQgZW5kIGNvb3JkaW5hdGVzIG9mIHRoZSBnZW5lIGJvZHkuIEltcG9ydGFudGx5LCB0aGUgZ2VuZSBib2R5IGlzIGV4dGVuZGVkIDVrYgp1cHN0cmVhbSBvZiB0aGUgVFNTLCB0byBhbHNvIGluY2x1ZGUgdGhlIHByb21vdGVyIHJlZ2lvbi4gVXNpbmcgdGhlIGVudGlyZSBnZW5lIApib2R5IGluc3RlYWQgb2Ygb25seSB0aGUgVFNTIGNhbiBiZSBhY2hpZXZlZCBieSBzZXR0aW5nICd1c2VUU1M9RkFMU0UnLiBJbiB0aGlzIAphcHByb2FjaCB0aGUgZ2VuZSB3aW5kb3cgaXMgY3JlYXRlZCBieSBleHRlbmRpbmcgLTEwMGtiIHVwc3RyZWFtIG9mIHRoZSBUU1MgLTVrYgphbmQgKzEwMGtiIGRvd25zdHJlYW0gb2YgdGhlIGdlbmUgZW5kIGNvb3JkaW5hdGUuIFRoaXMgd2F5LCB0aGUgZW50aXJlIGdlbmUgYm9keSAKd2lsbCBiZSBpbmNsdWRlZCBpbiB0aGUgZ2VuZSB3aW5kb3cuIEFuIHVud2FudGVkIGNvbnNlcXVlbmNlIG9mIHRoaXMgbWlnaHQgYmUKdGhhdCB2ZXJ5IGxhcmdlIGdlbmVzIGNvdWxkIGJpYXMgdGhlIGdlbmUgYWN0aXZpdHkgc2NvcmVzLiBUaGVyZWZvcmUgQXJjaFIgCmludHJvZHVjZXMgYSB3ZWlnaHQgZm9yIHRoZSBpbnZlcnNlIG9mIHRoZSBnZW5lIGJvZHkgc2l6ZSBhY2NvcmRpbmcgdG86CgokdyA9IFxmcmFjezF9e2dlbmUgc2l6ZX0kIHdpdGggJHckIGJlaW5nIHRoZSBpbnZlcnNlIG9mIHRoZSBnZW5lIHNpemUuCiQKCmdlbmVSZWdpb25zJGdlbmVXZWlnaHQgPC0gMSArIG0gKiAoZ2VuZVNjYWxlRmFjdG9yIC0gMSkgLyAobWF4KG0pIC0gbWluKG0pKSQKCkFkZGl0aW9uYWxseSwgQXJjaFIgdXNlcyBhIGRpc3RhbmNlIHdlaWdodC4gRmFydGhlciBhd2F5IHRpbGVzL3BlYWtzIGFyZSBsZXNzIGxpa2VseSB0byAKaW50ZXJhY3Qgd2l0aCBhIFRTUyB0aGFuIGNsb3NlciB0aWxlcy9wZWFrcy4gSWYgdGhlIGZpcnN0IGFwcHJvYWNoLCB1c2luZyBvbmx5IHRoZQpUU1MsIHRoZSBkaXN0YW5jZSB3ZWlnaHRzIGFyZSBjb21wdXRlZCBhcyBmb2xsb3dzOgoKJHdlaWdodCA9IGVeey0oYWJzKGRpc3RUU1MvNTAwMCkpfSQgd2l0aCAkZGlzdFRTUyQgYmVpbmcgdGhlIGRpc3RhbmNlIGZyb20gdGhlClRTUy4gVGhpcyB3YXkgdGhlIHdlaWdodHMgZGVjYXkgZXhwb25lbnRpYWxseSB3aXRoIGRpc3RhbmNlLiBUaGUgY29uc3RhbnQgdmFsdWUKb2YgJDUwMDAkIGlzIGEgcGFyYW1ldGVyIHdoaWNoIGNvdWxkIGJlIG9wdGltaXplZCBmb3IgZGlmZmVyZW50IGdlbmVzIG9yIGRhdGFzZXRzLCAKYnV0IGhlcmUgd2Ugd2lsbCBrZWVwIGl0IGNvbnN0YW50LiAKCkluIGNhc2UgdGhlIGVudGlyZSBnZW5lIGJvZHkgaXMgdXNlZCwgdGhlIGRpc3RhbmNlIHdlaWdodHMgYXJlIGtlcHQgY29uc3RhbnQgCmZvciBhbGwgdGlsZXMvcGVha3Mgd2l0aGluIHRoZSBnZW5lIGJvZHkgYW5kIG9ubHkgZGVjYXkgYmV5b25kIHRoZSBnZW5lIGJvZHkuCgokd2VpZ2h0ID0gXGJlZ2lue2Nhc2VzfSBpZiAoLTVrYiBmcm9tIFRTUywgVFRTKTogMSArIGVeey0xfSBcXCBlbHNlOiBlXnstYWJzKGRpc3RHQi81MDAwKSArIGVeey0xfX0gXGVuZHtjYXNlc30kCgoKCgojIyBBcmNoUiBHZW5lIEFjdGl2aXR5IFNjb3JlcyB1c2luZyBUQUQgYm91bmRhcmllcwoKSW5zdGVhZCBvZiB1c2luZyBhICsvLTEwMGtiIHdpbmRvdyBhcm91bmQgdGhlIGdlbmUgYm9keSwgaW4gdGhlIGFkYXB0ZWQgCmZ1bmN0aW9uIGFsbCBwZWFrcyB3aGljaCBhcmUgd2l0aGluIHRoZSBzYW1lIFRBRCBib3VuZGFyeSBhcyB0aGUgZ2VuZSBvZiBpbnRlcmVzdAphcmUgY29uc2lkZXJlZCBmb3IgdGhlIGFjdGl2aXR5IHNjb3JlIG9mIHRoYXQgZ2VuZS4gVGhlIGRpc3RhbmNlIHdlaWdodCB3aXRoIApjID0gNTAwMCBpcyBrZXB0IHRoZSBzYW1lIGFzIGZvciB0aGUgZGVmYXVsdCBBcmNoUiBmdW5jdGlvbi4gQXMgY2FuIGJlIHNlZW4gYmVsb3csCmV4dGVuZGluZyB0aGUgZ2VuZSB3aW5kb3cgdG8gVEFEIGJvdW5kYXJpZXMgeWllbGRzIHZlcnkgc2ltaWxhciByZXN1bHRzIGNvbXBhcmVkCnRvIHRoZSBkZWZhdWx0IEFyY2hSIGZ1bmN0aW9uLiAKCmBgYCN7cn0KcHJvaiA8LSBsb2FkQXJjaFJQcm9qZWN0KCIxMl9Db3B5MS8iKQoKIyBwcm9qIDwtIGFkZFRBREdlbmVTY29yZU1hdHJpeCgKIyAgIHByb2osCiMgICBnZW5lcyA9IGdldEdlbmVzKHByb2opLAojICAgcGVha3MgPSBnZXRQZWFrU2V0KHByb2opLAojICAgdGFkQm91bmRhcmllcyA9IHRhZF9ib3VuZGFyaWVzLAojICAgZ2VuZU1vZGVsID0gImV4cCgtYWJzKHgpLzUwMDApICsgZXhwKC0xKSIsCiMgICBtYXRyaXhOYW1lID0gIkdlbmVTY29yZU1hdHJpeCIsCiMgICBleHRlbmRVcHN0cmVhbSA9IGMoMTAwMCwgMTAwMDAwKSwKIyAgIGV4dGVuZERvd25zdHJlYW0gPSBjKDEwMDAsIDEwMDAwMCksCiMgICBnZW5lVXBzdHJlYW0gPSA1MDAwLCAjTmV3IFBhcmFtCiMgICAjZ2VuZURvd25zdHJlYW0gPSAwLCAjTmV3IFBhcmFtCiMgICB1c2VHZW5lQm91bmRhcmllcyA9IEZBTFNFLAojICAgdXNlVFNTID0gRkFMU0UsICNOZXcgUGFyYW0KIyAgIGV4dGVuZFRTUyA9IEZBTFNFLAojICAgdGlsZVNpemUgPSA1MDAsCiMgICBjZWlsaW5nID0gNCwKIyAgIGdlbmVTY2FsZUZhY3RvciA9IDUsICNOZXcgUGFyYW0KIyAgIHNjYWxlVG8gPSAxMDAwMCwKIyAgIGV4Y2x1ZGVDaHIgPSBjKCJjaHJZIiwgImNoclgiLCAiY2hyTSIpLAojICAgYmxhY2tsaXN0ID0gZ2V0QmxhY2tsaXN0KHByb2opLAojICAgdGhyZWFkcyA9IDEsCiMgICBwYXJhbGxlbFBhcmFtID0gTlVMTCwKIyAgIHN1YlRocmVhZGluZyA9IFRSVUUsCiMgICBmb3JjZSA9IFRSVUUsCiMgICBsb2dGaWxlID0gY3JlYXRlTG9nRmlsZSgiLmFkZFRBREdlbmVTY29yZU1hdCIpKQoKc2NvcmVzIDwtIGdldE1hdHJpeEZyb21Qcm9qZWN0KHByb2osIHVzZU1hdHJpeCA9ICJHZW5lU2NvcmVNYXRyaXgiKQpzY29yZV9tYXQgPC0gYXNzYXlzKHNjb3JlcylbWzFdXQpyb3duYW1lcyhzY29yZV9tYXQpIDwtIHJvd0RhdGEoc2NvcmVzKSRuYW1lCgoKI3NhdmVSRFMoc2NvcmVzLCAidGFkX3Njb3JlcyIpCmBgYAoKCmBgYCN7cn0KZ2dwbG90KCkgKwogIGdlb21faGlzdG9ncmFtKGFlcyh4ID0gcm93U3VtcyhzY29yZV9tYXQpKSwgYmlucyA9IDIwMCkKYGBgCgoKYGBgI3tyfQojIGNvbXB1dGUgYWdncmVnYXRlcyBvZiBBcmNoUiBnZW5lIGFjdGl2aXR5IHNjb3JlIG1hdHJpeApkZWZhdWx0X2FyY2hyIDwtIGNyZWF0ZV9zZWFjZWxsX2FnZ3JlZ2F0ZXMoYXJjaHJfc2NvcmVzX21hdCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlYWNlbGxzKQojIGNvbXB1dGUgYWdncmVnYXRlcyBmb3IgdGFkIGJvdW5kYXJ5IEFyY2hSIGdlbmUgYWN0aXZpdHkgc2NvcmUgbWF0cml4CnRhZF9hcmNociA8LSBjcmVhdGVfc2VhY2VsbF9hZ2dyZWdhdGVzKHNjb3JlX21hdCwgc2VhY2VsbHMpCiMgY29tcHV0ZSBhZ2dyZWdhdGVzIG9mIGdlbmUgZXhwcmVzc2lvbiBtYXRyaXgKcm5hX2h2ZyA8LSBjcmVhdGVfc2VhY2VsbF9hZ2dyZWdhdGVzKGV4cHJfc3ViLCBzZWFjZWxscykKCiMgY29ycmVsYXRpb24gYmV0d2VlbiBnZW5lIGV4cHJlc3Npb24gdmFsdWVzIGFuZCBkZWZhdWx0IEFyY2hyIGdlbmUgYWN0aXZpdHkgc2NvcmVzCmRlZmF1bHRfYXJjaHJfY29yciA8LSByb3d3aXNlX2NvcnJlbGF0aW9ucyhybmFfaHZnLCBkZWZhdWx0X2FyY2hyLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJBcmNoUiBnZW5lIGFjdGl2aXR5IHNjb3JlcywgU0VBY2VsbHMiKQojIGNvcnJlbGF0aW9uIGJldHdlZW4gZ2VuZSBleHByZXNzaW9uIGFuZCBUQUQgYm91bmRhcnkgZ2VuZSBhY3Rpdml0eSBzY29yZXMKdGFkX2NvcnIgPC0gcm93d2lzZV9jb3JyZWxhdGlvbnMocm5hX2h2ZywgdGFkX2FyY2hyLCAiQXJjaFIgZ2VuZSBhY3Rpdml0eSBzY29yZXMgd2l0aGluIFRBRCBib3VuZGFyaWVzLCBTRUFDZWxscyIpCgpjb3dwbG90OjpwbG90X2dyaWQoZGVmYXVsdF9hcmNocl9jb3JyW1syXV0sIHRhZF9jb3JyW1syXV0sIG5jb2wgPSAyKQoKZ2dwbG90KCkgKwogIGdlb21fcG9pbnQoYWVzKHggPSB0YWRfY29ycltbMV1dLCB5ID0gZGVmYXVsdF9hcmNocl9jb3JyW1sxXV1bbmFtZXModGFkX2NvcnJbWzFdXSldKSkgKwogIGdlb21fZGVuc2l0eV8yZF9maWxsZWQoYWVzKHggPSB0YWRfY29ycltbMV1dLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5ID0gZGVmYXVsdF9hcmNocl9jb3JyW1sxXV1bbmFtZXModGFkX2NvcnJbWzFdXSldKSwKICAgICAgICAgICAgICAgICAgICAgICAgIGFscGhhID0gLjUpICsKICBnZW9tX2xpbmUoYWVzKHggPSBkZWZhdWx0X2FyY2hyX2NvcnJbWzFdXSwgeSA9IGRlZmF1bHRfYXJjaHJfY29ycltbMV1dKSwgY29sID0gInJlZCIpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiTm9uZSIpICArCiAgbGFicyh4ID0gIkNvcnJlbGF0aW9uIGdlbmUgZXhwcmVzc2lvbiAmIEFyY2hSIFRBRCBib3VuZGFyeSBzY29yZXMiLAogICAgICAgIHRpdGxlID0gIlJlc3RyaWN0aW5nIEFyY2hSIHNjb3JlcyB0byB3aXRoaW4gVEFEIGJvdW5kYXJpZXMiLAogICAgICAgIHkgPSAiQ29ycmVsYXRpb24gZ2VuZSBleHByZXNzaW9uICYgQXJjaFIgZ2VuZSBhY3Rpdml0eSBzY29yZXMiKQoKYGBgCgoKIyMjIFRBRCBib3VuZGFyZXMgRTcuNQoKU2luY2UgdGhlIFRBRCBib3VuZGFyaWVzIHVzZWQgaGVyZSwgYXJlIGZyb20gZ2FzdHJ1bGF0aW9uIGRheSBFNy41LiBGb3IgdGhlIGxhdGVyCnRpbWUgcG9pbnRzIG5vIFRBRCBib3VuZGFyaWVzIGFyZSBhdmFpbGFibGUuIFRoZXJlZm9yZSwgaW4gdGhlIGZvbGxvd2luZyBJIHdpbGwgY2hlY2sgCmlmIHRoZSByZXN1bHRzIGltcHJvdmUgaW4gY29tcGFyaXNvbiB0byB0aGUgZGVmYXVsdCBBcmNoUiBmdW5jdGlvbiB3aGVuIHVzaW5nIG9ubHkgCmRhdGEgZnJvbSBFNy41LiBTaW5jZSBkdXJpbmcgZ2FzdHJ1bGF0aW9uIFRBRCBib3VuZGFyaWVzIG1pZ2h0IHN0aWxsIGJlIHZlcnkgCmR5bmFtaWMgdGhlIGltcHJvdmluZyBlZmZlY3Qgb2YgVEFEIGJvdW5kYXJpZXMgY291bGQgYmUgZGlsdXRlZCBieSBsYXRlciB0aW1lIHBvaW50cwppbiB0aGUgZGF0YS4gCgpXaGF0IGFyZSB0aCBnZW5lcyB3aGljaCBnZXQgemVybyBhY3Rpdml0eSBzY29yZXM/IERvIHRoZXkgbGllIG91dHNpZGUgdGhlIFRBRApib3VuZGFyaWVzPwoKYGBgI3tyfQplNzVfbWV0YSA8LSBjb2xEYXRhKHNjb3JlcykgJT4lIGFzLmRhdGEuZnJhbWUoKSAlPiUKICBmaWx0ZXIoU2FtcGxlICVpbiUgYygiRTcuNV9yZXAxIiwgIkU3LjVfcmVwMiIpKSAlPiUgCiAgcm93bmFtZXNfdG9fY29sdW1uKCJjZWxsIikKbWF0Xzc1IDwtIHNjb3JlX21hdFtyb3duYW1lcyhzY29yZV9tYXQpICVpbiUgcm93bmFtZXMoZXhwcl9zdWIpLCBlNzVfbWV0YSRjZWxsXQpzZWFjZWxsc19zdWIgPC0gc2VhY2VsbHMgJT4lIGZpbHRlcihpbmRleCAlaW4lIGNvbG5hbWVzKG1hdF83NSkpIAoKIyBjb21wdXRlIGFnZ3JlZ2F0ZXMgb2YgQXJjaFIgZ2VuZSBhY3Rpdml0eSBzY29yZSBtYXRyaXgKZGVmYXVsdF9hcmNociA8LSBjcmVhdGVfc2VhY2VsbF9hZ2dyZWdhdGVzKGFyY2hyX3Njb3Jlc19tYXRbcm93bmFtZXMoYXJjaHJfc2NvcmVzX21hdCkgJWluJQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJvd25hbWVzKGV4cHJfc3ViKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGU3NV9tZXRhJGNlbGxdLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VhY2VsbHNfc3ViKQojIGNvbXB1dGUgYWdncmVnYXRlcyBmb3IgdGFkIGJvdW5kYXJ5IEFyY2hSIGdlbmUgYWN0aXZpdHkgc2NvcmUgbWF0cml4CnRhZF9hcmNociA8LSBjcmVhdGVfc2VhY2VsbF9hZ2dyZWdhdGVzKG1hdF83NSwgc2VhY2VsbHNfc3ViKQojIGNvbXB1dGUgYWdncmVnYXRlcyBvZiBnZW5lIGV4cHJlc3Npb24gbWF0cml4CnJuYV9odmcgPC0gY3JlYXRlX3NlYWNlbGxfYWdncmVnYXRlcyhleHByX3N1YlssIGU3NV9tZXRhJGNlbGxdLCBzZWFjZWxsc19zdWIpCgojIGNvcnJlbGF0aW9uIGJldHdlZW4gZ2VuZSBleHByZXNzaW9uIHZhbHVlcyBhbmQgZGVmYXVsdCBBcmNociBnZW5lIGFjdGl2aXR5IHNjb3JlcwpkZWZhdWx0X2FyY2hyX2NvcnIgPC0gcm93d2lzZV9jb3JyZWxhdGlvbnMocm5hX2h2ZywgZGVmYXVsdF9hcmNociwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQXJjaFIgZ2VuZSBhY3Rpdml0eSBzY29yZXMsIFNFQWNlbGxzIikKIyBjb3JyZWxhdGlvbiBiZXR3ZWVuIGdlbmUgZXhwcmVzc2lvbiBhbmQgVEFEIGJvdW5kYXJ5IGdlbmUgYWN0aXZpdHkgc2NvcmVzCnRhZF9jb3JyIDwtIHJvd3dpc2VfY29ycmVsYXRpb25zKHJuYV9odmcsIHRhZF9hcmNociwgIkFyY2hSIGdlbmUgYWN0aXZpdHkgc2NvcmVzIHdpdGhpbiBUQUQgYm91bmRhcmllcywgU0VBQ2VsbHMiKQoKY293cGxvdDo6cGxvdF9ncmlkKGRlZmF1bHRfYXJjaHJfY29ycltbMl1dLCB0YWRfY29ycltbMl1dLCBuY29sID0gMikKCmdncGxvdCgpICsKICBnZW9tX3BvaW50KGFlcyh4ID0gdGFkX2NvcnJbWzFdXSwgeSA9IGRlZmF1bHRfYXJjaHJfY29ycltbMV1dW25hbWVzKHRhZF9jb3JyW1sxXV0pXSkpICsKICBnZW9tX2RlbnNpdHlfMmRfZmlsbGVkKGFlcyh4ID0gdGFkX2NvcnJbWzFdXSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeSA9IGRlZmF1bHRfYXJjaHJfY29ycltbMV1dW25hbWVzKHRhZF9jb3JyW1sxXV0pXSksCiAgICAgICAgICAgICAgICAgICAgICAgICBhbHBoYSA9IC41KSArCiAgZ2VvbV9saW5lKGFlcyh4ID0gZGVmYXVsdF9hcmNocl9jb3JyW1sxXV0sIHkgPSBkZWZhdWx0X2FyY2hyX2NvcnJbWzFdXSksIGNvbCA9ICJyZWQiKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIk5vbmUiKSAgKwogIGxhYnMoeCA9ICJDb3JyZWxhdGlvbiBnZW5lIGV4cHJlc3Npb24gJiBBcmNoUiBUQUQgYm91bmRhcnkgc2NvcmVzIiwKICAgICAgICB0aXRsZSA9ICJSZXN0cmljdGluZyBBcmNoUiBzY29yZXMgdG8gd2l0aGluIFRBRCBib3VuZGFyaWVzIiwKICAgICAgICB5ID0gIkNvcnJlbGF0aW9uIGdlbmUgZXhwcmVzc2lvbiAmIEFyY2hSIGdlbmUgYWN0aXZpdHkgc2NvcmVzIikKYGBgCgpXaGF0IGFyZSB0aGUgZ2VuZXMgd2hpY2ggZ2V0IHplcm8gY29ycmVsYXRpb24gd2l0aCBnZW5lIGV4cHJlc3Npb24/CgpUaGVyZSBhcmUgOCBnZW5lcyB3aGljaCBnZXQgemVybyBjb3JyZWxhdGlvbiB2YWx1ZXMgYmV0d2VlbiBnZW5lIGFjdGl2aXR5IHNjb3JlcwphbmQgZ2VuZSBleHByZXNzaW9uLiBUaGlzIGlzLCBiZWNhdXNlIHRoZXkgZ2V0IHplcm8gYWN0aXZpdHkgc2NvcmVzIGluIGFsbCBjZWxscy4gSG93ZXZlciwKdGhlIHNhbWUgZ2VuZXMgYXJlIGV4cHJlc3NlZCB0byBjZXJ0YWluIGxldmVscyBhY2NvcmRpbmcgdG8gdGhlIGdlbmUgZXhwcmVzc2lvbiAKbWF0cml4LiBUd28gb2YgdGhlIGdlbmVzIGFsc28gZ2V0IHplcm8gYWN0aXZpdHkgc2NvcmVzIGluIHRoZSBkZWZhdWx0IEFyY2hSIApmdW5jdGlvbiAoUHJsMmMzLCBHc2RtYzQpLiBUaGUgcmVhc29uIGZvciBpcyBub3QgaW1tZWRpYXRlbHkgY2xlYXIsIHNpbmNlIGFzIGxvbmcKYXMgdGhlcmUgYXJlIHBlYWtzIGluIGEgZ2VuZSB3aW5kb3csIHRoZSBkaXN0YW5jZSB3ZWlnaHQgd2lsbCBhdCBsZWFzdCBiZSAwLjM2IAphY2NvcmluZGcgdG8gdGhlIGZvcm11bGEuIE9uZSByZWFzb24gZm9yIHplcm8gdmFsdWVzIGNvdWxkIGJlIHRoYXQgdGhlc2UgZ2VuZXMgbGllIG91dHNpZGUgVEFEIApib3VuZGFyaWVzIHdpY2ggaXMgaW4gZmFjdCB0aGUgY2FzZSBmb3IgZm91ciBvdXQgb2YgOCBnZW5lcy4KCgpXaGF0IGlzIHRoZSBleHBsYW5hdGlvbiB3aHkgTHl6MiBhbmQgR20xMzU0NyAgZ2V0IGFjdGl2aXR5IHNjb3JlcyBvZiB6ZXJvPwoKYGBgI3tyfQp6ZXJvX2dlbmVzIDwtIG5hbWVzKHRhZF9jb3JyW1sxXV1bdGFkX2NvcnJbWzFdXSA9PSAwXSkKCnplcm9fbWF0IDwtIHNjb3JlX21hdFt6ZXJvX2dlbmVzLCBdCnJvd1N1bXMoemVyb19tYXQpIAoKCiMgY2hlY2sgdGhlIGRlZmF1bHQgQXJjaFIgc2NvcmVzIGZvciB0aGVzZSBnZW5lcwpyb3dTdW1zKGFyY2hyX3Njb3Jlc19tYXRbemVyb19nZW5lcywgXSkKCiMgY2hlY2sgdGhlIGdlbmUgZXhwcmVzc2lvbiBjb3V0bnMgZm9yIHRoZXNlIGdlbmVzCnJvd1N1bXMoZXhwcl9tYXRbemVyb19nZW5lcyxdKQoKCnAyZ19wb3MgPC0gcDJnICU+JSBhcy5kYXRhLmZyYW1lKCkgJT4lIGZpbHRlcihDb3JyZWxhdGlvbiA+IDApICU+JQogIHVuaXRlKGxpbmssIGlkeFJOQSwgaWR4QVRBQywgc2VwID0gIiUiLCByZW1vdmUgPSBGQUxTRSkKCmdlbmVfYW5ub19hbGwgPC0gcm93RGF0YShnZW5lX2V4cHIpICU+JSBhcy5kYXRhLmZyYW1lKCkgJT4lCiAgbXV0YXRlKGlkeFJOQSA9IHNlcShucm93KC4pKSkgJT4lIAogIGZpbHRlcihpZHhSTkEgJWluJSBwMmdfcG9zJGlkeFJOQSkgJT4lCiAgbXV0YXRlKHN0cmFuZCA9IGlmZWxzZShzdHJhbmQgPT0gMSwgIisiLCAiLSIpKSAlPiUKICBtdXRhdGUoc3RhcnRfY29vcmQgPSBpZmVsc2Uoc3RyYW5kID09ICIrIiwgc3RhcnQsIGVuZCkpICU+JSAKICByZW5hbWUoZ2VuZSA9IG5hbWUpICMlPiUgR1JhbmdlcygpCgojIHN1YnNldCBhdGFjIGdyYW5nZXMgJiBnZXQgbWlkZGxlIG9mIGVhY2ggcGVhawpwb3NfYXRhY19ncmFuZ2VzX2FsbCA8LSBtZXRhZGF0YShwMmcpW1sxXV0gICU+JSAKICBhcy5kYXRhLmZyYW1lKCkgJT4lCiAgbXV0YXRlKGlkeEFUQUMgPSBzZXEobnJvdyguKSkpICU+JSAKICAjIGdyb3VwX2J5KHNlcW5hbWVzKSAlPiUKICAjIG11dGF0ZShpZHggPSBzZXFfYWxvbmcoc2VxbmFtZXMpKSAlPiUgCiAgIyB1bmdyb3VwICU+JQogICN0aWR5cjo6dW5pdGUoY2hyX2lkeCwgc2VxbmFtZXMsIGlkeCwgcmVtb3ZlID0gRkFMU0UsIHNlcCA9ICJfIikgJT4lIAogIGZpbHRlcihpZHhBVEFDICVpbiUgcDJnX3BvcyRpZHhBVEFDKSAlPiUgCiAgbXV0YXRlKG1pZGRsZSA9IHN0YXJ0ICsgMzAwKSAjJT4lIEdSYW5nZXMoKSAKCgoKIyBjb21iaW5lIHRoZSB0aHJlZSBkYXRhZnJhbWVzCnAyZ19qb2luX2FsbCA8LSBsZWZ0X2pvaW4ocDJnX3BvcywgYXMuZGF0YS5mcmFtZShwb3NfYXRhY19ncmFuZ2VzX2FsbCksCiAgICAgICAgICAgICAgICAgICAgICBieSA9ICJpZHhBVEFDIikKcDJnX2pvaW5fYWxsIDwtIGxlZnRfam9pbihwMmdfam9pbl9hbGwsIGFzLmRhdGEuZnJhbWUoZ2VuZV9hbm5vX2FsbCksCiAgICAgICAgICAgICAgICAgICAgICBieSA9ICJpZHhSTkEiLCBzdWZmaXggPSBjKCIuYXRhYyIsICIucm5hIikpCgoKcDJnX2pvaW5fYWxsIDwtIHAyZ19qb2luX2FsbCAlPiUgCiAgbXV0YXRlKGRpc3RhbmNlID0gYWJzKHN0YXJ0X2Nvb3JkIC0gbWlkZGxlKSkKCgoKICAjIGZpbmQgb3ZlcmxhcHBpbmcgcGVha3MgYW5kIGdlbmUgd2luZG93IGluIGNocm9tb3NvbWUtYXdhcmUgZmFzaGlvbgp0YWRfb3ZlcmxhcHNfZ2VuZXMgPC0gKGZpbmRPdmVybGFwcyhnZW5lX2Fubm9fYWxsICU+JSBHUmFuZ2VzKCksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGFkX2JvdW5kYXJpZXMpKQoKCiMgZ2V0IGFsbCBnZW5lcyB3aGljaCBhcmUgZm91bmQgd2l0aGluIHRhZCBib3VkbmFyaWVzCmdlbmVfYW5ub193aXRoaW5fdGFkIDwtIGdlbmVfYW5ub19hbGxbcXVlcnlIaXRzKHRhZF9vdmVybGFwc19nZW5lcyksXQoKCiMgTGV0cyBleGFtaW5lIHRoZSBnZW5lcyB3aGljaCBhcmUgZm91bmQgd2l0aGluIHRhZCBib3VuZGFyaWVzLCBidXQKIyBnZXQgYW4gYWN0aXZpdHkgc2NvcmUgb2YgemVybyBuZXZlcnRoZWxlc3MKZ2VuZV9hbm5vX3dpdGhpbl90YWQgJT4lIGZpbHRlcihnZW5lICVpbiUgemVyb19nZW5lcykKCgpnZW5lX25hbWUgPSAiTHl6MiIKY2hyX25hbWUgPSAiY2hyMiIKY2hyeCA8LSB0YWRfYm91bmRhcmllcyAlPiUgYXMuZGF0YS5mcmFtZSgpICU+JSBmaWx0ZXIoc2VxbmFtZXMgPT0gY2hyX25hbWUpICU+JUdSYW5nZXMoKQpoaXRzIDwtIGZpbmRPdmVybGFwcyhnZW5lX2Fubm9fYWxsICU+JSBmaWx0ZXIoZ2VuZSA9PSBnZW5lX25hbWUpICU+JSBHUmFuZ2VzKCksICBjaHJ4KQpzdGFydF90YWQgPC0gc3RhcnQoY2hyeFtzdWJqZWN0SGl0cyhoaXRzKSxdKQplbmRfdGFkIDwtIGVuZChjaHJ4W3N1YmplY3RIaXRzKGhpdHMpLF0pCnN0YXJ0X2dlbmUgPC0gc3RhcnQoZ2VuZV9hbm5vX2FsbCAlPiUgZmlsdGVyKGdlbmUgPT0gZ2VuZV9uYW1lKSAlPiUgR1JhbmdlcygpKQplbmRfZ2VuZSA8LSBlbmQoZ2VuZV9hbm5vX2FsbCAlPiUgZmlsdGVyKGdlbmUgPT0gZ2VuZV9uYW1lKSAlPiUgR1JhbmdlcygpKQpwcmludChwYXN0ZTAoIk91dCBvZiAiLCBsZW5ndGgoemVyb19nZW5lcyksICIgZ2VuZXMsICAiLCAgbGVuZ3RoKHplcm9fZ2VuZXNbemVyb19nZW5lcyAlaW4lIGdlbmVfYW5ub193aXRoaW5fdGFkJGdlbmVdKSAsICIgZ2VuZXMgYXJlIGZvdW5kIHdpdGhpbiB0YWQgYm91bmRhcmllcywgd2hpbGUgdGhlIHJlc3QgYXJlIG5vdC4iKSkKcG9zX2F0YWNfZ3Jhbmdlc19hbGwgJT4lIGFzLmRhdGEuZnJhbWUoKSAlPiUgZmlsdGVyKHNlcW5hbWVzID09IGNocl9uYW1lKSAlPiUKICBmaWx0ZXIoc3RhcnQgPiBzdGFydF90YWQgJiBlbmQgPCBlbmRfdGFkKQoKIyAKIyB6ZXJvX2dlbmVzCiMgCiMgaWR4IDwtIChnZW5lX2Fubm9fYWxsICU+JSBmaWx0ZXIoZ2VuZSAlaW4lIHplcm9fZ2VuZXMpKSRpZHhSTkEKIyAKIyBpZHggJWluJSBnZW5lX2Fubm9fYWxsW3RhZF9vdmVybGFwc19nZW5lcyRxdWVyeUhpdHMsCmBgYAoKCgojIyBBcmNoUiBHZW5lIEFjdGl2aXR5IFNjb3JlcyB1c2luZyBnZW5lIGJvZHkKCjxkZXRhaWxzPgo8c3VtbWFyeT5BcmNoUiBHZW5lIEFjdGl2aXR5IFNjb3JlcyB1c2luZyBnZW5lIGJvZHk8L3N1bW1hcnk+CgpgYGAje3J9Cgojc2F2ZUFyY2hSUHJvamVjdChBcmNoUlByb2ogPSBwcm9qLCBvdXRwdXREaXJlY3RvcnkgPSAiMTJfQ29weTQvIiwgbG9hZCA9IEZBTFNFKQpsb2FkQXJjaFJQcm9qZWN0KCIxMl9hY3Rpdml0eV9zY29yZXNfZ2VuZV9ib2R5X3BlYWtzLyIpCgpwcm9qIDwtIGFkZEthdGhpR2VuZVNjb3JlTWF0cml4KAogIHByb2osCiAgZ2VuZXMgPSBnZXRHZW5lcyhwcm9qKSwKICBwZWFrcyA9IGdldFBlYWtTZXQocHJvaiksCiAgZ2VuZU1vZGVsID0gImV4cCgtYWJzKHgpLzUwMDApICsgZXhwKC0xKSIsCiAgbWF0cml4TmFtZSA9ICJHZW5lU2NvcmVNYXRyaXgiLAogIGV4dGVuZFVwc3RyZWFtID0gYygxMDAwLCAxMDAwMDApLAogIGV4dGVuZERvd25zdHJlYW0gPSBjKDEwMDAsIDEwMDAwMCksCiAgI2dlbmVVcHN0cmVhbSA9IDUwMDAsICNOZXcgUGFyYW0KICAjZ2VuZURvd25zdHJlYW0gPSAwLCAjTmV3IFBhcmFtCiAgdXNlR2VuZUJvdW5kYXJpZXMgPSBUUlVFLAogIHVzZVRTUyA9IEZBTFNFLCAjTmV3IFBhcmFtCiAgZXh0ZW5kVFNTID0gRkFMU0UsCiAgdGlsZVNpemUgPSA1MDAsCiAgY2VpbGluZyA9IDQsCiAgZ2VuZVNjYWxlRmFjdG9yID0gNSwgI05ldyBQYXJhbQogIHNjYWxlVG8gPSAxMDAwMCwKICBleGNsdWRlQ2hyID0gYygiY2hyWSIsICJjaHJNIiksCiAgYmxhY2tsaXN0ID0gZ2V0QmxhY2tsaXN0KHByb2opLAogIHRocmVhZHMgPSAxLAogIHBhcmFsbGVsUGFyYW0gPSBOVUxMLAogIHN1YlRocmVhZGluZyA9IFRSVUUsCiAgZm9yY2UgPSBUUlVFLAogIGxvZ0ZpbGUgPSBjcmVhdGVMb2dGaWxlKCIuYWRkS2F0aGlHZW5lU2NvcmVNYXQiKSkKCgpzY29yZXMgPC0gZ2V0TWF0cml4RnJvbVByb2plY3QocHJvaiwgdXNlTWF0cml4ID0gIkdlbmVTY29yZU1hdHJpeCIpCgpzY29yZXNfbWF0IDwtIGFzc2F5cyhzY29yZXMpW1sxXV0Kcm93bmFtZXMoc2NvcmVzX21hdCkgPC0gcm93RGF0YShzY29yZXMpJG5hbWUKCgojIHNjZSA8LSBTaW5nbGVDZWxsRXhwZXJpbWVudChsaXN0KHNjb3Jlcz1zY29yZXNfbWF0KSwKIyAgICAgICAgICAgICAgICAgICAgICAgICAgIHJvd0RhdGEgPSBhcy5kYXRhLmZyYW1lKHJvd0RhdGEoc2NvcmVzKSksCiMgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xEYXRhID0gYXMuZGF0YS5mcmFtZShjb2xuYW1lcyhzY29yZXNfbWF0KSkpCiMgCiMgd3JpdGVINUFEKHNjZSwgIi9vbWljcy9ncm91cHMvT0UwNTMzL2ludGVybmFsL2thdGhhcmluYS9zY0RvUkkvZ2FzdHJ1bGF0aW9uX2RhdGEvanVweXRlcl9ub3RlYm9va3MvcDJnX2dlbmVfYWN0aXZpdHlfc2NvcmVzL2FyY2hyX3Njb3Jlc19nZW5lX2JvZHlfcGVha19iYXNlZCIsIFhfbmFtZSA9ICJzY29yZXMiKQoKYGBgCjwvZGV0YWlscz4KCkNvcnJlbGF0aW5nIGdlbmUgZXhwcmVzc2lvbiB3aXRoIGFjdGl2aXR5IHNjb3JlczoKCmBgYCN7cn0KYXJjaHJfZ2VuZV9ib2R5X2FnZyA8LSBrbm5fYWdncmVnYXRlcyhzY29yZXNfbWF0LCBjZWxsX2FnZ19saXN0KQoKZ2VuZV9ib2R5X2tubiA8LSByb3d3aXNlX2NvcnJlbGF0aW9ucyhybmFfYWdnLCBhcmNocl9nZW5lX2JvZHlfYWdnLCAiQXJjaFIgZ2VuZSBhY3Rpdml0eSBzY29yZXMgYmFzZWQgb24gcGVhayBtYXRyaXgsIHVzaW5nIGdlbmUgYm9keSIpCgoKY293cGxvdDo6cGxvdF9ncmlkKGFyY2hyX2tubltbMl1dLCBnZW5lX2JvZHlfa25uW1syXV0sIG5jb2wgPSAyKQoKcDEgPC0gZ2dwbG90KCkgKyBnZW9tX2RlbnNpdHlfMmRfZmlsbGVkKGFlcyh4ID0gZ2VuZV9ib2R5X2tubltbMV1dLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5ID0gYXJjaHJfa25uW1sxXV0pLCBhbHBoYSA9IC41KSArCiAgZ2VvbV9wb2ludChhZXMoeCA9IGdlbmVfYm9keV9rbm5bWzFdXSwgeSA9IGFyY2hyX2tubltbMV1dKSkgKwogIGdlb21fbGluZShhZXMoeCA9IGdlbmVfYm9keV9rbm5bWzFdXSwgeSA9IGdlbmVfYm9keV9rbm5bWzFdXSksIGNvbCA9ICJyZWQiKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIk5vbmUiKSAKYGBgCgoKIyMgQXJjaFIgR2VuZSBBY3Rpdml0eSBTY29yZXMgdXNpbmcgVFNTLCBubyBnZW5lIGJvZHkKCjxkZXRhaWxzPgo8c3VtbWFyeT5BcmNoUiBHZW5lIEFjdGl2aXR5IFNjb3JlcyB1c2luZyBUU1MsIG5vIGdlbmUgYm9keTwvc3VtbWFyeT4KCgpgYGAje3J9Cgpwcm9qIDwtIGxvYWRBcmNoUlByb2plY3QoIjEyX2FjdGl2aXR5X3Njb3Jlc19UU1NfdGlsZXMvIikKCnByb2ogPC0gYWRkR2VuZVNjb3JlTWF0cml4KAogIHByb2osCiAgZ2VuZXMgPSBnZXRHZW5lcyhwcm9qKSwKICBnZW5lTW9kZWwgPSAiZXhwKC1hYnMoeCkvNTAwMCkiLAogIG1hdHJpeE5hbWUgPSAiR2VuZVNjb3JlTWF0cml4IiwKICBleHRlbmRVcHN0cmVhbSA9IGMoMTAwMCwgMTAwMDAwKSwKICBleHRlbmREb3duc3RyZWFtID0gYygxMDAwLCAxMDAwMDApLAogICNnZW5lVXBzdHJlYW0gPSA1MDAwLCAjTmV3IFBhcmFtCiAgI2dlbmVEb3duc3RyZWFtID0gMCwgI05ldyBQYXJhbQogIHVzZUdlbmVCb3VuZGFyaWVzID0gVFJVRSwKICB1c2VUU1MgPSBUUlVFLCAjTmV3IFBhcmFtCiAgZXh0ZW5kVFNTID0gRkFMU0UsCiAgdGlsZVNpemUgPSA1MDAsCiAgY2VpbGluZyA9IDQsCiAgZ2VuZVNjYWxlRmFjdG9yID0gNSwgI05ldyBQYXJhbQogIHNjYWxlVG8gPSAxMDAwMCwKICBleGNsdWRlQ2hyID0gYygiY2hyWSIsICJjaHJNIiksCiAgYmxhY2tsaXN0ID0gZ2V0QmxhY2tsaXN0KHByb2opLAogIHRocmVhZHMgPSAxLAogIHBhcmFsbGVsUGFyYW0gPSBOVUxMLAogIHN1YlRocmVhZGluZyA9IFRSVUUsCiAgZm9yY2UgPSBUUlVFLAogIGxvZ0ZpbGUgPSBjcmVhdGVMb2dGaWxlKCIuYWRkR2VuZVNjb3JlTWF0cml4IikpCgoKc2NvcmVzIDwtIGdldE1hdHJpeEZyb21Qcm9qZWN0KHByb2osIHVzZU1hdHJpeCA9ICJHZW5lU2NvcmVNYXRyaXgiKQoKc2NvcmVzX21hdCA8LSBhc3NheXMoc2NvcmVzKVtbMV1dCnJvd25hbWVzKHNjb3Jlc19tYXQpIDwtIHJvd0RhdGEoc2NvcmVzKSRuYW1lCgoKIyBzY2UgPC0gU2luZ2xlQ2VsbEV4cGVyaW1lbnQobGlzdChzY29yZXM9c2NvcmVzX21hdCksCiMgICAgICAgICAgICAgICAgICAgICAgICAgICByb3dEYXRhID0gYXMuZGF0YS5mcmFtZShyb3dEYXRhKHNjb3JlcykpLAojICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sRGF0YSA9IGFzLmRhdGEuZnJhbWUoY29sbmFtZXMoc2NvcmVzX21hdCkpKQojIAojIHdyaXRlSDVBRChzY2UsICIvb21pY3MvZ3JvdXBzL09FMDUzMy9pbnRlcm5hbC9rYXRoYXJpbmEvc2NEb1JJL2dhc3RydWxhdGlvbl9kYXRhL2p1cHl0ZXJfbm90ZWJvb2tzL3AyZ19nZW5lX2FjdGl2aXR5X3Njb3Jlcy9hcmNocl9zY29yZXNfdHNzIiwgWF9uYW1lID0gInNjb3JlcyIpCgpgYGAKCiMjIEFyY2hSIGdlbmUgYWN0aXZpdHkgc2NvcmVzIGNvbXB1dGVkIHVzaW5nIFRTUywgbm8gZ2VuZSBib2R5IGFuZCBQZWFrTWF0cml4IGluc3RlYWQgb2YgVGlsZU1hdHJpeAoKPGRldGFpbHM+CjxzdW1tYXJ5PkFyY2hSIGdlbmUgYWN0aXZpdHkgc2NvcmVzIGNvbXB1dGVkIHVzaW5nIFRTUywgbm8gZ2VuZSBib2R5IGFuZCBQZWFrTWF0cml4IGluc3RlYWQgb2YgVGlsZU1hdHJpeDwvc3VtbWFyeT4KCmBgYCN7cn0KcHJvaiA8LSBsb2FkQXJjaFJQcm9qZWN0KCIxMl9hY3Rpdml0eV9zY29yZXNfVFNTX3BlYWtzLyIpCgpwcm9qIDwtIGFkZEthdGhpR2VuZVNjb3JlTWF0cml4KAogIHByb2osCiAgZ2VuZXMgPSBnZXRHZW5lcyhwcm9qKSwKICBwZWFrcyA9IGdldFBlYWtTZXQocHJvaiksCiAgZ2VuZU1vZGVsID0gImV4cCgtYWJzKHgpLzUwMDApIiwKICBtYXRyaXhOYW1lID0gIkdlbmVTY29yZU1hdHJpeCIsCiAgZXh0ZW5kVXBzdHJlYW0gPSBjKDEwMDAsIDEwMDAwMCksCiAgZXh0ZW5kRG93bnN0cmVhbSA9IGMoMTAwMCwgMTAwMDAwKSwKICAjZ2VuZVVwc3RyZWFtID0gNTAwMCwgI05ldyBQYXJhbQogICNnZW5lRG93bnN0cmVhbSA9IDAsICNOZXcgUGFyYW0KICB1c2VHZW5lQm91bmRhcmllcyA9IFRSVUUsCiAgdXNlVFNTID0gVFJVRSwgI05ldyBQYXJhbQogIGV4dGVuZFRTUyA9IEZBTFNFLAogIHRpbGVTaXplID0gNTAwLAogIGNlaWxpbmcgPSA0LAogIGdlbmVTY2FsZUZhY3RvciA9IDUsICNOZXcgUGFyYW0KICBzY2FsZVRvID0gMTAwMDAsCiAgZXhjbHVkZUNociA9IGMoImNoclkiLCAiY2hyTSIpLAogIGJsYWNrbGlzdCA9IGdldEJsYWNrbGlzdChwcm9qKSwKICB0aHJlYWRzID0gMSwKICBwYXJhbGxlbFBhcmFtID0gTlVMTCwKICBzdWJUaHJlYWRpbmcgPSBUUlVFLAogIGZvcmNlID0gVFJVRSwKICBsb2dGaWxlID0gY3JlYXRlTG9nRmlsZSgiLmFkZEthdGhpR2VuZVNjb3JlTWF0IikpCgpzY29yZXMgPC0gZ2V0TWF0cml4RnJvbVByb2plY3QocHJvaiwgdXNlTWF0cml4ID0gIkdlbmVTY29yZU1hdHJpeCIpCgpzY29yZXNfbWF0IDwtIGFzc2F5cyhzY29yZXMpW1sxXV0Kcm93bmFtZXMoc2NvcmVzX21hdCkgPC0gcm93RGF0YShzY29yZXMpJG5hbWUKCiMKIyBzY2UgPC0gU2luZ2xlQ2VsbEV4cGVyaW1lbnQobGlzdChzY29yZXM9c2NvcmVzX21hdCksCiMgICAgICAgICAgICAgICAgICAgICAgICAgICByb3dEYXRhID0gYXMuZGF0YS5mcmFtZShyb3duYW1lcyhzY29yZXNfbWF0KSksCiMgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xEYXRhID0gYXMuZGF0YS5mcmFtZShjb2xuYW1lcyhzY29yZXNfbWF0KSkpCiMgCiMgd3JpdGVINUFEKHNjZSwgIi9vbWljcy9ncm91cHMvT0UwNTMzL2ludGVybmFsL2thdGhhcmluYS9zY0RvUkkvZ2FzdHJ1bGF0aW9uX2RhdGEvanVweXRlcl9ub3RlYm9va3MvcDJnX2dlbmVfYWN0aXZpdHlfc2NvcmVzL2FyY2hyX3Njb3Jlc19wZWFrX2Jhc2VkIiwgWF9uYW1lID0gInNjb3JlcyIpCmBgYAoKYGBgI3tyfQoKCgojIHNjZSA8LSBTaW5nbGVDZWxsRXhwZXJpbWVudChsaXN0KHAyZ19tYXQgPSBwMmdfbWF0KSkKIyAKIyB3cml0ZUg1QUQoc2NlLCAiL29taWNzL2dyb3Vwcy9PRTA1MzMvaW50ZXJuYWwva2F0aGFyaW5hL3NjRG9SSS9nYXN0cnVsYXRpb25fZGF0YS9qdXB5dGVyX25vdGVib29rcy9wMmdfZ2VuZV9hY3Rpdml0eV9zY29yZXMvcDJnX21hdF8yNTBrYiIsCiMgICAgICAgICAgIFhfbmFtZSA9ICJwMmdfbWF0IikKCiMgCiMgCiMgc2NlIDwtIFNpbmdsZUNlbGxFeHBlcmltZW50KGxpc3QocGVha19tYXQgPSBwZWFrX21hdCkpCiMgCiMgd3JpdGVINUFEKHNjZSwgIi9vbWljcy9ncm91cHMvT0UwNTMzL2ludGVybmFsL2thdGhhcmluYS9zY0RvUkkvZ2FzdHJ1bGF0aW9uX2RhdGEvanVweXRlcl9ub3RlYm9va3MvcDJnX2dlbmVfYWN0aXZpdHlfc2NvcmVzL3BlYWtfbWF0IiwKIyAgICAgICAgICAgWF9uYW1lID0gInBlYWtfbWF0IikKCgojIGNwX25hbWVzIDwtIGNvbG5hbWVzKGNvbERhdGEoZ2VuZV9leHByKSkKIyBjcF9uYW1lc1syMF0gPC0gImNlbGx0eXBlcyIKIyBjb2xuYW1lcyhjb2xEYXRhKGdlbmVfZXhwcikpIDwtIGNwX25hbWVzCgpzY2UgPC0gU2luZ2xlQ2VsbEV4cGVyaW1lbnQobGlzdChnZW5lcyA9IGV4cHJfbWF0KSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgI3Jvd0RhdGEgPSBhcy5kYXRhLmZyYW1lKHJvd25hbWVzKGdlbmVfZXhwcikpLAogICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xEYXRhID0gYXMuZGF0YS5mcmFtZShjb2xEYXRhKGdlbmVfZXhwcikpKQoKIyB3cml0ZUg1QUQoc2NlLCAiL29taWNzL2dyb3Vwcy9PRTA1MzMvaW50ZXJuYWwva2F0aGFyaW5hL3NjRG9SSS9nYXN0cnVsYXRpb25fZGF0YS9qdXB5dGVyX25vdGVib29rcy9wMmdfZ2VuZV9hY3Rpdml0eV9zY29yZXMvZ2VuZV9leHByX21hdCIsCiMgICAgICAgICAgIFhfbmFtZSA9ICJnZW5lcyIpCiMgCiMgCiMgI3AyZ19tYXRfbm9ybSA8LSBwMmdfbWF0IC8gcm93U3VtcyhwMmdfbWF0KQojIHNjb3JlcyA8LSBwMmdfbWF0ICUqJSBwZWFrX21hdAojIHNjb3JlcyA8LSB0KHQoc2NvcmVzKSAvIGNvbFN1bXMoc2NvcmVzKSkKIyBzdG9waWZub3QoYW55KGlzLm5hKHNjb3JlcykpID09IEZBTFNFKQojIHNjb3Jlc0B4IDwtIHBtaW4oMWU5LCBleHAoc2NvcmVzQHgpIC0gMSkKIyAKIyAKIyAKIyBzY2UgPC0gU2luZ2xlQ2VsbEV4cGVyaW1lbnQobGlzdChpbnZlc3RpZ2F0aW9uID0gaW52ZXN0aWdhdGlvbikpCiMgCiMgd3JpdGVINUFEKHNjZSwgIi9vbWljcy9ncm91cHMvT0UwNTMzL2ludGVybmFsL2thdGhhcmluYS9zY0RvUkkvZ2FzdHJ1bGF0aW9uX2RhdGEvanVweXRlcl9ub3RlYm9va3MvcDJnX2dlbmVfYWN0aXZpdHlfc2NvcmVzL2ludmVzdGlnYXRpb25fc2NvcmVzIiwKIyAgICAgICAgICAgWF9uYW1lID0gImludmVzdGlnYXRpb24iKQoKCgoKCiMgbGF0ZW50IGVtYmVkZGluZwplbWIgPC0gZ2V0UmVkdWNlZERpbXMoCiAgQXJjaFJQcm9qID0gcHJvaiwKICByZWR1Y2VkRGltcyA9ICJhdGFjX0xTSV8xMDAwMDAiLAogIHJldHVybk1hdHJpeCA9IFRSVUUsCiAgZGltc1RvVXNlID0gMTozMCwKICBzY2FsZURpbXMgPSBOVUxMLAogIGNvckN1dE9mZiA9IDAuNzUKKQpkaW0oZW1iKQoKCnNjZSA8LSBTaW5nbGVDZWxsRXhwZXJpbWVudChsaXN0KGVtYmVkZGluZyA9IGVtYikpCgp3cml0ZUg1QUQoc2NlLCAiL29taWNzL2dyb3Vwcy9PRTA1MzMvaW50ZXJuYWwva2F0aGFyaW5hL3NjRG9SSS9nYXN0cnVsYXRpb25fZGF0YS9qdXB5dGVyX25vdGVib29rcy9wMmdfZ2VuZV9hY3Rpdml0eV9zY29yZXMvYXJjaHJfbHNpX2VtYmVkZGluZyIsCiAgICAgICAgICBYX25hbWUgPSAiZW1iZWRkaW5nIikKCgpgYGA=